VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • PythonI/O进阶学习笔记_10.python的多线程(2)

6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE None 25 0 LOAD_FAST 0 (a) 2 LOAD_CONST 1 (1) 4 INPLACE_SUBTRACT 6 STORE_FAST 0 (a) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE None
复制代码
从字节码来看流程为:#1.load a #2.load 1 #3.add  #4.赋值给a
任何一步字节码都是有可能被切换出去另外一个线程的字节码去操作a,可能在1线程运行到4字节码(a和1相加)的时候,开始运行2线程的6字节码(赋值给a)。
类似的有银行存取钱、商品库存等也会有这个问题。
 
2.2 线程如何同步?
用锁将这段代码段锁住,锁住时,不进行切换。直接运行完这段代码段。
 
a.Lock和Rlock
threading中有提供lock。
复制代码
复制代码
import threading
from threading import  Lock
total=0
lock=Lock()
def add():
    global total
    global lock
    for i in range(1000000):
        lock.acquire()
        total += 1
        lock.release()
def desc():
    global  total
    global lock
    for i in range(1000000):
        lock.acquire()
        total -= 1
        lock.release()
 
if __name__=="__main__":
    add_total=threading.Thread(target=add)
    desc_total=threading.Thread(target=desc)
    add_total.start()
    desc_total.start()
 
    add_total.join()
    desc_total.join()
    print(total)
    pass
复制代码
复制代码
注意acquire和release成对存在。运行的时候会发现比不加锁的时候慢比较多。所以其实锁的问题也很明显:锁会影响性能,锁会引起死锁。死锁里有个非常常见的问题资源竞争是很容易发生的。
那能不能我锁里套着锁呢?Lock方法是不可以的,但是threading提供了Rlock可重入锁。
Rlock在同一个线程里面,可以连续调用多次acquire,但是注意acquire和release也一定是要成对存在的。
复制代码
from threading import  RLock
 
total=0
lock=RLock()
def add():
    global total
    global lock
    for i in range(1000000):
        lock.acquire()
        lock.acquire()
        total += 1
        lock.release()
        lock.release()
复制代码
 
3.condition使用以及源码分析
condition是条件变量,用于复杂的线程间同步。
 
3.1 condition的使用
例子:现有一个需求,要求 天猫精灵和小爱一人一句进行对话。如果我们现用lock来实现是没办法做到这边说完一句,那边就说一句的。所以有了condition。
在这个例子中,需要用到condition的两个重要方法 notify()和wait()。notify()用于通知这边动作完成,wait()用于阻塞住等待消息。
复制代码
#input
import  threading
 
class XiaoAi(threading.Thread):
    def __init__(self,cond):
        self.cond=cond
        super().__init__(name="小爱")
    def run(self):
        with self.cond:
            print("小爱: 天猫在吗 我是小爱")
            self.cond.notify()  #小爱print完了,信号发送
            self.cond.wait()  #小爱等待接受信号
            print("小爱: 我们来背诗吧")
            self.cond.notify()
class TianMao(threading.Thread):
    def __init__(self,cond):
        self.cond=cond
        super().__init__(name="天猫")
 
    def run(self):
        with self.cond:
            self.cond.wait()
            print("天猫: 在 我是天猫")
            self.cond.notify()
            self.cond.wait()
            print("天猫: 好啊")
            self.cond.notify()
 
if __name__=="__main__":
    condition=threading.Condition()
    xiaoai=XiaoAi(condition)
    tianmao=TianMao(condition)
 
    tianmao.start()
    xiaoai.start()
 
#output:
小爱: 天猫在吗 我是小爱
天猫: 在 我是天猫
小爱: 我们来背诗吧
天猫: 好啊
复制代码
ps:需要注意的是
  • condition必须先with 再调用 notify和wait方法
  • 这么写的时候,线程的start()顺序很重要
 
3.2 Condition源码分析
condition其实是有两层锁的。一把底层锁,会在线程调用了wait()的时候释放。
上层锁会在wait()的时候放入双端队列中,在调用notify()的时候被唤醒。
 
a.condition=threading.Condition()
condition初始化的时候申请了一把锁
 
b.self.cond.wait()
先释放了condition初始化的时候申请的底层锁,然后又申请了锁放入双端队列。