Python菜鸟编程第十七课之多线程变量共享
1.多线程—共享全局变量问题
1.1
假设有两个线程t1和t2,都要对一个变量g_num进行运算(+1),两个线程t1和t2分别对g_num+1
demo:
import threading
import time
g_num = 0
def work1(num):
global g_num
for i in range(num):
g_num += 1
print('---in work1,g_num is %d----' % g_num)
def work2(num):
global g_num
# t_start=time.time()
for i in range(num):
g_num += 1
print('---in work2,g_num is %d----' % g_num)
# t_end = time.time()
# print('work2 运行时间:',t_end-t_start)
def main():
print('线程创建之前g_num 的值为:%d' % g_num)
t1 = threading.Thread(target=work1, args=(10,))
t2 = threading.Thread(target=work2, args=(10,))
# t1 = threading.Thread(target=work1)
# t2 = threading.Thread(target=work2)
t1.start()
t2.start()
while len(threading.enumerate())!=1:
time.sleep(1)
# time.sleep(5)
print('线程创建之后g_num 的值为:%d' % g_num)
if __name__ == '__main__':
main()
运行结果:
线程创建之前g_num 的值为:0
---in work1,g_num is 10----
---in work2,g_num is 20----
线程创建之后g_num 的值为:20
一种可能的情况:
在num=0时,t1取得num=0,此时系统把t1调度为“sleeping"的状态,t2转换为”runing"的状态,t2也获得num=0。然后t2对得到的值+1,并赋值给num,num=1。然后系统又将t2调度为“sleeping"的状态,把t1转换为"running",线程t1又把他之前得到的0加1后赋值给num。这种情况,明明两个线程都完成一次+1工作,但结果还是num=1。
当我们将两个进程的参数调整为1000000时,每次运行的结果都不相同。说明多个线程同时对一个全局变量进行操作,会出现资源竞争问题,从而数据结果会不正确。导致线程安全问题。
2.同步
同步就是协同步调。按照预定的先后次序进行运行。
进程和线程同步,可以理解为进程或者线程A和B一块配合,A执行到一定程度时需要依赖B的某个结果让自己暂时停止运行让B运行,B运行后,再将结果给A,A再继续操作。如此往复,直到程序结束。
2.1计算错误的解决
思路:
- 系统调度t1,获取num=0,此时上一把锁,即不予许其他线程操作num
- num+1
- 解锁,此时num=1,其他线程就可以使用num了,此时num=1
- 同理其他线程在对num修改时,也要先上锁,处理完后再解锁。在上锁的过程中,不允许其他线程访问。这就确保了数据的准确性。
2.2互斥锁
当多个线程几乎同时修改某个共享数据时,需要同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制就是引入同步锁。
2.3死锁
在线程间共享多个资源时,如果两个线程分别占用一部分资源,并且同时等待对方的资源,就会造成死锁。
死锁一般很少发生,但一旦发生就会造成应用停止响应。
demo:
import threading
import time
printer_mutex = threading.Lock() # 打印机锁
paper_mutext = threading.Lock() # 纸张锁
class ResumeThread(threading.Thread):
"""编写个人简历任务的线程"""
def run(self):
print("ResumeThread:编写个人简历任务")
# 使用打印机资源,先对打印机加锁
printer_mutex.acquire()
print("--ResumeThread:正在使用打印机资源--")
time.sleep(1) # 休眠1秒
# 使用纸张耗材,先对纸张耗材加锁
paper_mutext.acquire()
print("--正在使用纸张资源--")
time.sleep(1)
paper_mutext.release() # 释放纸张锁
# 释放打印机锁
printer_mutex.release()
class PaperListThread(threading.Thread):
"""盘点纸张耗材任务的线程"""
def run(self):
print("PaperListThread:盘点纸张耗材任务")
# 使用纸张耗材,先对纸张耗材加锁
paper_mutext.acquire()
print("--PaperListThread:正在盘点纸张耗材--")
time.sleep(1) # 休眠1秒
# 使用打印机资源,打印清单
printer_mutex.acquire()
print("--正在使用打印机资源--")
time.sleep(1)
printer_mutex.release() # 释放打印机锁
# 释放纸张耗材锁
paper_mutext.release()
if __name__ == '__main__':
t1 = ResumeThread()
t2 = PaperListThread()
t1.start()
t2.start()