注:如果您能仔细的读完本文后,您可能对多线程有更深刻的理解
相信大多数人对于多线程总是感到头痛
于是今天我冒着头痛的危险,详细的分析了python中多线程在运行时候的流程
我们先上一个简单的代码(后面我们会用多线程来搞一个buffer缓存器)
我们首先定义两个函数:m、n
def m(): print("m 开始工作") time.sleep(10) print("m 工作完毕") def n(): print("n 开始工作") time.sleep(5) print("n 工作完毕")如果直接调用这两个方法
m() n()结果肯定是下面这个结果
m 开始工作 m 工作完毕 n 开始工作 n 工作完毕当然,还有时间等待的部分。(这里没法显示了)
如果我们将这个过程变成多线程的形式,打印的结果又是什么呢?
这里我们需要了解,如何定义多线程
首先我们需要导入线程的这个模块import threading
其次,我们定义线程的方法,将方法的名字放入到线程的方法中
thread = threading.Thread(target=m) thread1 = threading.Thread(target=n)开启线程
thread.start() thread1.start()使用join方法,让主线程等待子线程结束后再执行主程序
thread.join() thread1.join()最后我们在主线程中添加一句话
print('after start()')这样多线程的代码就调用完毕了
这时,我们在运行一下:
m 开始工作 n 开始工作 n 工作完毕 m 工作完毕 after start()这个结果,显而易见:
mn两个进程同时开始工作,因为n需要的时间比较少,所以先返回(可以理解先报告工作完毕了)
只有当m完成后,主程序才执行结束。
当然,如果不加thread.join()的方法,主线程会直接完成,不会顾及子线程是否完成
可以举个例子:
m和n是员工,主线程是老板。
如果加上join方法,老板是在监工,并且m和n两个员工都做完,才去吃饭
如果不加上join方法,老板则是下发任务后,直接就去吃饭了,不会管m、n两个员工是否完成任务
通过以上的步骤,我们大概的了解了什么是多线程了
当然多线程还有一个很重要的概念就是锁的概念
我们举一个例子来说明一下
# -*-coding:utf-8-*- import threading import time n = 0 lock = threading.Lock() def add(): global n for _ in range(400000): n += 1 def sub(): global n for _ in range(400000): n -= 1 if __name__ == "__main__": thread_add = threading.Thread(target=add) thread_sub = threading.Thread(target=sub) thread_add.start() thread_sub.start() thread_sub.join() thread_add.join() print("n = ", n, flush=True)上面的代码主要是这样的:
使用多线程的方式进行两个函数方法的同时使用,一个是不断的给n加上1,一个是不断的给n减去1
当然两个循环的次数是相同的
大家不难猜测:既然给n加上四十万个1,然后在对n减去四十万个1,最终的结果一定是0呀。
好的,我们运行一下这个代码:
然而现实总是残酷的,打印的结果如下: 是什么问题导致的这种原因的呢??
实际上是这样的
我么可以这样理解,甲乙两个人在一共工厂工作,甲的任务是对n进行加一,乙的任务是对n进行减一。
当然,在工作的位置中,甲看不到乙,乙也看不到甲
当甲取到n的值,并将n的值进行加一的操作后,将结果返回,如果在此之间乙已将n的值进行减一操作的话
那么,数值将会被覆盖掉。换成了甲加一之后的值,这样,在这种不断覆盖的过程中,最终n的值当然不是0了
当然以上这是一个问题,但是该如何解决呢?我们引出了锁的概念
首先锁是如何定义的呢?
lock = threading.Lock()同样对于锁,我们有两种操作,上锁和解锁
lock.acquire() # 上锁 lock.release() # 解锁如果学过操作系统的小伙伴,你一定直到PV操作,和这个简直就是异曲同工
这时我们将上述程序进行锁的操作(注意上锁和解锁的位置)
# -*-coding:utf-8-*- import threading import time n = 0 lock = threading.Lock() def add(): global n for _ in range(400000): lock.acquire() n += 1 lock.release() def sub(): global n for _ in range(400000): lock.acquire() n -= 1 lock.release() if __name__ == "__main__": thread_add = threading.Thread(target=add) thread_sub = threading.Thread(target=sub) thread_add.start() thread_sub.start() thread_sub.join() thread_add.join() print("n = ", n, flush=True)此时打印的结果为: 装上锁后,进程之间的运行就是正常的了
这也就相当于,甲工作的时候开始上锁,乙不能动,等到甲运行结束后,乙就开始工作了,从而程序就正常的运行了。