上篇文章中子线程之间出现资源竞争的原因是CPU占用问题,一个子线程的代码没执行完就被切换掉,解决此问题的方法是,让代码全部执行完再切换,或者根本不让执行,不能执行到一半切换另一个线程。原则同银行转账,转账时借钱人把钱转出去,但是收款人没收到,杜绝这种情况。
同步:协同一起去做事情,有步骤,约定好过程,“你先说完,我再说”
如何实现同步?
互斥锁:用threading里的Lock创建,即:
#创建锁,默认是没上锁的
mutex = threading.Lock()
#锁定
mutex.acquire()
#解锁
mutex.release()
上锁后,其它子线程只能堵塞,解锁后解阻塞
说明:
互斥锁也是一个变量,放在函数外面,就是一个全局变量,哪个子线程想用,就可以放到代码里直接中,只是用而不是修改全局变量,不用加global声明
思路:
给两个子线程都上锁,让两个线程去抢,谁先抢到谁先上锁,不只是给第一个子线程上锁
把有资源竞争的地方,都用锁套起来,即:
mutex.acquire()
for i in range(num):
g_num += 1
mutex.release()
注意:
创建锁时,Lock只有首字母是大写,其它是小写,否则运行程序时会报错:threading没有LOCK属性
查看模块中的属性:进入ipython3交互模式,import导入模块,dir(模块名称),例如:进入ipython3后,import
threading dir(threading),即可看到弹出一个列表,里面是属性
1 import threading
2 import time
3
4 #定义一个全局变量
5 g_num = 0
6
7 def test1(num):
8 global g_num
9 #上锁,如果之前没有被上锁,那么上锁成功
10 #如果之前已经被上锁,那么此时会堵塞在这里,知道锁被解开
11 mutex.acquire()
12 for i in range(num):
13 g_num += 1
14 # 解锁
15 mutex.release()
16 print("--test1--%s" % g_num)
17
18
19 def test2(num):
20 global g_num
21 mutex.acquire()
22 for i in range(num):
23 g_num += 1
24 mutex.release()
25 print("--test1--%s" % g_num)
26 print("--test2--%s" % g_num)
27
28
29 #创建互斥锁,默认没有上锁
30 mutex = threading.Lock()
31
32
33 def main():
34
35 t1 = threading.Thread(target=test1,args=(1000000,))
36 t2 = threading.Thread(target=test2,args=(1000000,))
37
38 t1.start()
39 t2.start()
40
41 time.sleep(5)
42
43 print("--main--%s" % g_num)
44 if __name__ == "__main__":
45 main()
以上代码是对for循环部分进行上锁,意味着当所有的for完成后才会解锁,当循环时间足够长的时候,另一个线程就要等很长时间。
上锁的原则,上的代码越少越好。
修改代码,只把原来出问题的那一句代码上锁,即:
把上锁解锁放到for循环里,只对+1进行上锁
for i in range(num):
mutex.acquire()
g_num += 1
# 解锁
mutex.release()
1 import threading
2 import time
3
4 #定义一个全局变量
5 g_num = 0
6
7 def test1(num):
8 global g_num
9 #上锁,如果之前没有被上锁,那么上锁成功
10 #如果之前已经被上锁,那么此时会堵塞在这里,知道锁被解开
11 for i in range(num):
12 mutex.acquire()
13 g_num += 1
14 # 解锁
15 mutex.release()
16 print("--test1--%s" % g_num)
17
18
19 def test2(num):
20 global g_num
21 for i in range(num):
22 mutex.acquire()
23 g_num += 1
24 # 解锁
25 mutex.release()
26 print("--test2--%s" % g_num)
27
28
29 #创建互斥锁,默认没有上锁
30 mutex = threading.Lock()
31
32
33 def main():
修改代码后运行的结果如下:
–test1–1968722
–test2–2000000
–main–2000000
虽然test1是加完了100000次之后才进行的打印,但是在1000000执行过程中,test2也在对全局变量做更改,导致test1执行完指定次数后,输入的结果大于100000,但主线程打印出来的全局变量的最终结果是200000