5、线程之间共享全局变量
# TODO 线程之间共享全局变量 import threading import time my_list=[] #写入数据 def write_data(): for i in range(5): my_list.append(i) time.sleep(0.1) print("write_data",my_list) # 读取数据 def read_data(): print("read_data",my_list) if __name__ == '__main__': # 创建写入数据线程 write_dataThred=threading.Thread(target=write_data) # 创建读取数据线程 read_dataThred=threading.Thread(target=read_data) #开启进程 write_dataThred.start() # 主线程等待写入线程执行完成以后代码再继续往下执行 write_dataThred.join()# 等待子线程执行之后再执行下一次代码,不加这行你会发现read_data 读不到数据 print("开始读取数据")
结果:read_data 哪去了??????从上图可以发现只要重新启动read_dataThred线程就可以了你难道认为这样就可以了吗,太天真了。-_-下面再引入一个例子。
# TODO 线程之间共享全局变量出现问题 问题:线程不一致,交替拿变量 解决方法 保持线程同步,同一时刻只能有一个线程去操作全局变量 两种方式 线程等待 + 互斥所 import threading # 创建函数,实现循环100万次,每次全局变量加一 g_num=0 # 每次全局变量加一 def sun_num(): for i in range(1000000): global g_num g_num+=1 print("sun_num",g_num) # 每次全局变量加一 def sun_num2(): for i in range(1000000): global g_num g_num+=1 print("sun_num2", g_num) if __name__ == '__main__': sun_numThred=threading.Thread(target=sun_num) sun_num2Thred=threading.Thread(target=sun_num2) #开启线程 sun_numThred.start() sun_num2Thred.start()
结果:结果是出结果了,但是你们细看一下代码,你们不感觉这个答案有问题吗?有没有这样一个疑问,for循环,遍历共享变量,最后应该是1000000和2000000啊,怎么打印出来的是1000000和1456202,而且细心的你还会发现每次的大男孩不一样。想要解决问题应该怎么办呢?问题总结:线程不一致,交替拿变量 解决方法 保持线程同步,同一时刻只能有一个线程去操作全局变量 两种方式 线程等待 + 互斥所线程等待:join出场ticle/details/123494962
# TODO 线程之间共享全局变量出现问题 问题:线程不一致,交替拿变量 解决方法 保持线程同步,同一时刻只能有一个线程去操作全局变量 两种方式 线程等待 + 互斥所 import threading # 创建函数,实现循环100万次,每次全局变量加一 g_num=0 # 每次全局变量加一 def sun_num(): for i in range(1000000): global g_num g_num+=1 print("sun_num",g_num) # 每次全局变量加一 def sun_num2(): for i in range(1000000): global g_num g_num+=1 print("sun_num2", g_num) if __name__ == '__main__': sun_numThred=threading.Thread(target=sun_num) sun_num2Thred=threading.Thread(target=sun_num2) #开启线程 sun_numThred.start() # # TODO 方法一 sun_numThred.join() sun_num2Thred.start()
结果:线程等待,当前线程等待其他线程执行某些操作,典型场景就是生产者消费者模式,在任务条件不满足时,等待其他线程的操作从而使得条件满足。等到其他线程完成操作释放后再分配线程。当前线程获得资源继续执行操作。
6、互斥锁
****互斥锁 Lock():在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
互斥锁三步骤 ~~~ 创建一把锁==》上锁==》释放锁 三步走
# TODO 互斥锁 Lock() import threading # # TODO 创建一把锁==》上锁==》释放锁 三步走 # mutext=threading.Lock() # mutext.acquire() # mutext.release() #定义全局变量 g_num=0 # 创建一把锁 mutext=threading.Lock() # 每次全局变量加一 def sun_num(): # TODO 上锁 mutext.acquire() for i in range(1000000): global g_num g_num+=1 print("sun_num",g_num) # TODO 释放锁 mutext.release() # 每次全局变量加一 def sun_num2(): # TODO 上锁 mutext.acquire() for i in range(1000000): global g_num g_num+=1 print("sun_num2", g_num) if __name__ == '__main__': sun_numThred=threading.Thread(target=sun_num) sun_num2Thred=threading.Thread(target=sun_num2) #开启线程 sun_numThred.start() sun_num2Thred.start()
结果:是不是非常神奇。主义事项:一、互斥锁就三步骤 1、创建一把锁 2、上锁 3、释放锁 。二、保证共享数据操作的完整性三、等当前上锁线程执行完成,释放锁后其他线程才能申请资源
7、死锁
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
# TODO 死锁 import threading import time # 创建互斥锁 lock=threading.Lock() def get_value(index): #上锁 lock.acquire() print(threading.current_thread()) my_list=[3,4,7,2] # 根据下标释放取值 if index>=len(my_list): print("下标越界:",index) # TODO 当下标越界了释放锁,让后面线程继续取值 # lock.release() return value=my_list[index] print(value) time.sleep(0.2) #释放锁 # lock.release() if __name__ == '__main__': #模拟大量线程取值 for i in range(30): sub_thred=threading.Thread(target=get_value,args=(i,)) sub_thred.start()
结果:注释掉释放锁的操作,线程刚执行就卡住了,产生了死锁。
打开释放所操作
# TODO 死锁 import threading import time # 创建互斥锁 lock=threading.Lock() def get_value(index): #上锁 lock.acquire() print(threading.current_thread()) my_list=[3,4,7,2] # 根据下标释放取值 if index>=len(my_list): print("下标越界:",index) # TODO 当下标越界了释放锁,让后面线程继续取值 lock.release() return value=my_list[index] print(value) time.sleep(0.2) #释放锁 lock.release() if __name__ == '__main__': #模拟大量线程取值 for i in range(30): sub_thred=threading.Thread(target=get_value,args=(i,)) sub_thred.start()
结果:1、因为我们是用方式传参,循环30次,索引0~29。
2、我们会发现我们在判断越界后,因为释放锁所以仍会输出。
3、加入互斥锁解除了死锁危机。