高级编程技巧 学习笔记
一、共享全局变量资源竞争
- 一个线程写入,一个线程读取,没问题,如果两个线程都写入呢?
import threading
def demo1(nums):
global num
for i in range(nums):
num += 1
print('demo1---num:%d' % num)
def demo2(nums):
global num
for i in range(nums):
num += 1
print('demo2--num:%d' % num)
def main():
t1 = threading.Thread(target=demo1, args=(100,))
t2 = threading.Thread(target=demo2, args=(100,))
t1.start()
t2.start()
print("main----num:%d" % num)
if __name__ == '__main__':
num = 0
main()
- 当 num=100 时,执行结果与我们预想的一样(100,200,200)
- 当 num=1000000 很大时,结果却有出入
这就是 “共享全局变量资源竞争” 引起的问题。
# 代码 num += 1 执行时, 其实是分成三个语句执行的
1.获取 num 的值
2.把获取到的 num +1
3.把第2步的结果从新赋值给 num
# cpu 处理线程时是使用时间片轮转的方式
# 因此, 当线程的时间片用完时 num+=1 代码并未执行完成
如何解决呢?使用互斥锁
二、互斥锁
-
创建锁
mutex = threading.Lock()
-
锁定
mutex.acquire()
-
解锁
mutex.release()
import threading
import time
def demo1(nums):
global num
# 加锁
mutex.acquire()
for i in range(nums):
num += 1
# 解锁
mutex.release()
print('demo1---num:%d' % num)
def demo2(nums):
global num
# 加锁
mutex.acquire()
for i in range(nums):
num += 1
# 解锁
mutex.release()
print('demo2--num:%d' % num)
def main():
t1 = threading.Thread(target=demo1, args=(1000000,))
t2 = threading.Thread(target=demo2, args=(1000000,))
t1.start()
t2.start()
# time.sleep(1)
print("main----num:%d" % num)
if __name__ == '__main__':
num = 0
# 创建锁
mutex = threading.Lock()
main()
# main----num:164353
# demo1---num:1000000
# demo2--num:2000000
为什么 main 打印出来的结果还是不对??
因为子线程执行的同时,主进程也再继续执行,所以 main 打印出来的值有可能是 demo2 执行过程中的某一个 num 值,而不是最终的 num 值。
取消上面的 time.sleep(1)
注释在执行,对比结果。(注循环的次数越大,则等待的时间这越长)
三、死锁
3.1、死锁实例
- 在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。
import threading
import time
class MyThread1(threading.Thread):
def run(self):
# 对mutexA上锁
mutexA.acquire()
# mutexA上锁后,延时1秒,这时另外那个线程已经把mutexB上锁
print(self.name+'----do1---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexB已经被另外的线程抢先上锁了,未解锁前不能重新上锁
mutexB.acquire()
print(self.name+'----do1---down----')
mutexB.release()
# 对mutexA解锁
mutexA.release()
class MyThread2(threading.Thread):
def run(self):
# 对mutexB上锁
mutexB.acquire()
# mutexB上锁后,延时1秒,这时另外那个线程 把mutexA上锁
print(self.name+'----do2---up----')
time.sleep(1)
# 此时会堵塞,因为这个mutexA已经被另外的线程抢先上锁了,未解锁前不能重新上锁
mutexA.acquire()
print(self.name+'----do2---down----')
mutexA.release()
# 对mutexB解锁
mutexB.release()
if __name__ == '__main__':
mutexA = threading.Lock()
mutexB = threading.Lock()
t1 = MyThread1()
t2 = MyThread2()
t1.start()
t2.start()
3.2、避免死锁
-
程序设计时要尽量避免
-
添加超时时间等