Python线程详解

在正式介绍线程之前我们,首先要了解一下进程与线程的关系,以及分线程等等种种概念

进程:每个程序都会有一个进程负责管理程序各个功能的执行,进程只会有一个。

线程:每个进程里面至少有一个线程,称为主线程,除此之外,还会有其他的线程,称之为分线程。

如果读者对于线程基本概念不太了解,可以参看一下知名博主阮一峰的一篇博客:《进程与线程的一个简单解释》

1.线程的基本操作。

Python中多线程主要有两个模块,_thread和threading模块,前者底层一点,后者是我们常用的模块,能够满足绝大部分的编程需求。我们主要围绕threading这个模块展开介绍,线程的启动运行,需要去创建一个Thread对象,然后调用该对象的start()方法。

首先我们来我们来看一下当前进程的代码。

import threading
print('当前线程为',threading.current_thread().name)

点击运行,输出结果为:当前线程为 MainThread。这个就是当前线程的名称。

import threading
def fun(n):
    while n > 0:
        print("线程name:", threading.current_thread().name, "参数n:", n)
        n -= 1
t = threading.Thread(target=fun, args=(5,))
t.start()
#线程name: Thread-1 参数n: 5
#线程name: Thread-1 参数n: 4
#线程name: Thread-1 参数n: 3
#线程name: Thread-1 参数n: 2
#线程name: Thread-1 参数n: 1

在Thread中形参中,形参target传入函数名,args传入函数对应的参数,这个参数必须是可迭代的对象,如果是一个元组,且元组内只有一个参数,那么必须在参数后加上逗号,此逗号不可省略。

开辟新的线程。

import threading
def myThread():
    print('位置1', threading.current_thread().name)
    print('位置2', threading.current_thread().name)
myThread()
sub_thread=threading.Thread(target=myThread,name='newThread')
sub_thread.start()

#位置1 MainThread
#位置2 MainThread
#位置1 newThread
#位置2 newThread

threading.Thread开辟一个新的线程,name为新线程的名称。

import threading
def myThread():
    print('位置1', threading.current_thread().name)
    print('位置2', threading.current_thread().name)
sub_thread=threading.Thread(target=myThread,name='newThread')
sub_thread.start()
# sub_thread.join()
print('outside1',threading.current_thread().name)
print('outside2',threading.current_thread().name)

#位置1 newThread
#outside1 MainThread
#位置2 newThread
#outside2 MainThread

输出的结果有可能是交叉的,也有可能是不交叉的,当程序运行的时候,主线程和分线程的任务是交叉进行的,彼此不会相互影响。

把代码中的sub_thread.join()解掉注释,输出的结果是

只会是这一种情况。jion()的作用就是让主线程去等待其他的线程,主线程会在所有的的线程都结束之后,再运行主线程。

2.线程锁

当我们用多个线程去修改同一个数据的时候,这个时候对出现线程与线程之间的竞争,任何一个线程的改变,都会对其他的线程造成影响,如果我们想要得到我们想要的结果,在其中一个线程运行完之前,其他的线程都不能先对数据进行改动,那么我们就需要对这个线程去加上一个线程锁。

import threading
import time
import random
count=0
def get_money(money):
    global count
    count+=money
    count+=money
    count-=money
def lock_thread(money):
    time.sleep(random.randint(1,3))
    print('当前线程为', threading.current_thread().name)
    get_money(money)
    time.sleep(random.randint(1, 3))
    print('当前线程为', threading.current_thread().name)
thread1=threading.Thread(target=lock_thread,name='thread1',args=(1000,))
thread2=threading.Thread(target=lock_thread,name='thread2',args=(2000,))
thread1.start()
thread2.start()
#当前线程为 thread2
#当前线程为 thread1
#当前线程为 thread1
#当前线程为 thread2

如上代码,输出的结果会多种多样的,毫无顺序可言,为了想要得到我们想要的结果,我们要对此加上一个线程锁。

lock = threading.Lock()     # Lock对象
lock.acquire()              # 锁定
lock.release()              # 解锁

 

当状态为解锁时,acquire()将状态更改为锁定并立即返回。当状态被锁定时,acquire()块直到对另一个协程中的release()的调用将其改变为解锁,然后acquire()调用将其重置为锁定并返回。

import threading
import time
import random
count=0
def get_money(money):
    global count
    count+=money
    count+=money
    count-=money
lock=threading.Lock()
def lock_thread(money):
    lock.acquire()
    time.sleep(random.randint(1,3))
    print('当前线程为', threading.current_thread().name)
    get_money(money)
    time.sleep(random.randint(1, 3))
    print('当前线程为', threading.current_thread().name)
    # 解锁
    lock.release()
thread1=threading.Thread(target=lock_thread,name='thread1',args=(1000,))
thread2=threading.Thread(target=lock_thread,name='thread2',args=(2000,))
thread1.start()
thread2.start()
# print('hello world')
#当前线程为 thread1
#当前线程为 thread1
#当前线程为 thread2
#当前线程为 thread2

如上代码所示,输出的结果只会有这一个形式,必须thread1完成后,thread2才会执行。在代码下方有一个被注释掉的主线程, print('hello world')。解开注释后,将会先输出hello world,然后再输出上方的结果。与join相比,lock更加注重局部,某一个线程还没有走完,其他的线程就不能使用。join注重的是整体,线程1没有执行完,线程2就不能执行。

3.线程队列

import queue
# 创建一个线程队列
# 队列FIFO,frist in  frist  out
q=queue.Queue()
for i  in range(5):
    # 将内容放入到线程队列中
    q.put(i)
while not q.empty():
    print(q.get())
#0
#1
#2
#3
#4

这种队列是属于先进先出类型的。

import queue
p=queue.LifoQueue()
 # LIFO last in frist out 后进先出
for i in range(5):
    p.put(i)
while not p.empty():
     print(p.get())
#4
#3
#2
#1
#0

这种队列是属于后进先出类型的。

4.介绍yield与return之间有何区别

yield与return的相同点,都是可以返回函数执行的结果,不同点就是return在返回结果后,结束函数的运行,而yield则是让函数变成了一个生成器,返回值后,继续向下运行。通过yield传递值得方法是一个迭代的对象。下面看例子。

def test1(num):
    print('123456')
    return num
    print('return结束')
num=test1('9')
print(num)
#123456
#9

这个是return 的用法,return下面的print('return结束')并没有输出。

def test2(n):
    for i in range(n):
        yield i*i
        print('结束')
for i in test2(4):
    print(i)
#0
#结束
#1
#结束
#4
#结束
#9
#结束
#16
#结束
可以看到每次得到一个返回之后,依然向下运行,输出结束两字。

yield相比于return,可以使用的更加灵活,反应也会更加迅速。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay丶萧邦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值