python深度探究线程和线程锁的概念

    技术2022-07-11  78

    注:如果您能仔细的读完本文后,您可能对多线程有更深刻的理解


    相信大多数人对于多线程总是感到头痛

    于是今天我冒着头痛的危险,详细的分析了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)

    此时打印的结果为: 装上锁后,进程之间的运行就是正常的了

    这也就相当于,甲工作的时候开始上锁,乙不能动,等到甲运行结束后,乙就开始工作了,从而程序就正常的运行了。

    Processed: 0.010, SQL: 9