概述
通过 python-多线程修改公共变量引发的血案 可见,多线程修改公共变量时,会引发bug,为了解决bug,要用到线程同步
- 第一步,先让一个线程读变量num的值
注意:具体是哪个线程不一定,由操作系统调度,图中由 线程1 执行 - 第二步,让该线程计算后将值写入内存
- 第三步,然后让另一个线程读变量num的值
- 第四步,让该线程计算后将值写入内存
临界资源
某些公共资源只能一个线程访问操作,这样的资源称为临界资源(互斥资源,共享变量)
如:上面的商品数量、票号,或者打印机
临界区
访问临界资源的那段代码称为临界区
如:上面访问商品数量、票号的代码
线程互斥
由于多线程要共享资源,而有些资源需要互斥使用,因此各线程间竞争使用这些资源
如:多线程修改公共变量,多线程访问打印机等
线程同步
多线程访问操作公共资源时,需要多个线程有顺序的执行,互相合作、互相等待,共同完成一项任务。
一般来说,同步往往包含互斥
使用互斥锁
为了实现多线程同步和互斥,python提出互斥锁
当一个线程申请互斥锁后,就只有该线程运行,其他线程都阻塞,直到该线程释放锁时,其他线程才会继续执行
由此可见,申请互斥锁和释放互斥锁之间的代码,只有一个线程在运行,所以互斥锁不要滥用,否则会导致多线程效率低下
Lock类的使用说明
threading模块中定义了Lock类,实现互斥锁,让线程同步
注意:当 多个线程 修改 公共变量时,才需要互斥锁
创建锁
lock=threading.Lock()
获得锁
表示之后的代码全部被锁定,只能一个线程执行
lock.acquire([blocking=True,timeout=-1])
参数:
- blocking:为True,表示当前线程会阻塞,直到获得锁为止。
False,当前线程不会阻塞,直接返回false
默认为True - timeout:当blocking为True时有意义,表示n秒后返回false
释放锁
表示之后的代码,多线程都可以同时执行
lock.release()
改造之前的代码
#coding=utf-8
import threading
def modify():
global num
global lock
#获得锁,之后的代码只能有一个线程执行,具体是哪个线程不一定,由os调度
lock.acquire()
t=threading.current_thread()
#获取当前线程对象
t=threading.current_thread()
for index in range(100000):
num+=1
print('%s,num=%d'%(t.name,num))
#释放锁,之后的代码可以多线同时执行
lock.release()
if __name__ == "__main__":
num=0
#创建锁
lock=threading.Lock()
#创建线程,此线程修改全局变量
t=threading.Thread(target=modify)
#创建线程,此线程打印全局变量
t2=threading.Thread(target=modify)
#启动线程
t.start()
t2.start()
#阻塞
t.join()
t2.join()
print('执行结束,num=%d'%(num))
改造卖火车票代码
#coding=utf-8
import threading
import time
import os
#票总数
count=500
#窗口数量
windownum=3
#初始化票号码
num=1
def sell(index):
global num,lock
print('%d号售票口-开始售票,票号:%d'%(index,num))
#修改变量
while True:
lock.acquire()
if num<=count:#本行代码是临界区,要在上面加锁
print('%d号售票口-售出票号:%d'%(index,num))
i=num+1
# time.sleep(0.00001)
num=i
print('%d号售票口-下一票号:%d'%(index,num))
lock.release()
else:
print('%d号售票口-售罄'%(index))
lock.release()
break
if __name__ == "__main__":
lock=threading.Lock()
stime=time.time()
ts=[]
for index in range(windownum):
t=threading.Thread(target=sell,args=(index,))
ts.append(t)
for item in ts:
item.start()
for index in ts:
item.join()
#主进程打印值
print('主进程,num=%s'%num)
etime=time.time()
print(etime-stime)
