2020.10.5 多线程
多任务可以由多进程完成,也可以由一个进程内的多线程完成。
Python的标准库提供了两个模块:_thread与threading,_thread是低级模块,threading是高级模块,对_thread完成了封装。大多数情况下,只需用到threading这个高级模块即可。
启动一个线程:把一个函数传入并创建Thread实例,然后调用start()开始执行。
import time,threading #新线程执行的代码 def loop(): print('Thread %s is running'%threading.current_thread().name) n=0 while n<5: n+=1 print('Thread %s => %d'%(threading.current_thread().name,n)) time.sleep(1) print('Thread %s ended'%threading.current_thread().name) print('Thread %s is running...'%threading.current_thread().name) t=threading.Thread(target=loop,name='LoopThread') t.start() t.join() print('Thread %s ended'%threading.current_thread().name)
Thread MainThread is running... Thread LoopThread is running Thread LoopThread => 1 Thread LoopThread => 2 Thread LoopThread => 3 Thread LoopThread => 4 Thread LoopThread => 5 Thread LoopThread ended Thread MainThread ended
由于任何进程默认就会启动一个线程,故把这个线程称为主线程,即我们上文看到的MainThread,主线程又可以启动新的线程,python的threading模块有一个current_thread()函数,它永远返回当前线程的实例,子线程的名称在通过threading.Thread()创建时指定,本例中我们用LoopThread命名子线程。名字只在打印时起作用,完全没有其他作用,如果不起名字Python就自动给线程命名为Thread-1、Thread-2......
Lock
多线程和多进程的区别在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响。
多线程中,所有变量都由所有线程共享,因此任何一个变量都可以被任意一个线程修改,因此,线程间的数据共享的最大危害在于多个线程同时修改一个变量,把内容给改乱了。
这就涉及到OS中提到的同步、互斥的概念了。
举个例子:
import time, threading #假定银行存款额为balance balance=0 #先存后取 理论上balance应该不变 def change_it(n): global balance balance+=n balance-=n def run_thread(n): for x in range(200000): change_it(n) t1=threading.Thread(target=run_thread,args=(5,)) t2=threading.Thread(target=run_thread,args=(8,)) t1.start() t2.start() t1.join() t2.join() print(balance)
结果:
8
我们创建了了两个线程,先存后取,理论上结果应该为0。但是由于线程的调度是由OS决定的,当t1、t2交替执行时,只要循环的次数足够多,balance的结果就不一定是0了。
如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,由于该线程被我们加了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放,并且该线程获得锁后才能修改。由于只有一个锁,所以同一时刻最多只能有一个线程持有锁,因此就不会引发冲突。
创建一个锁通过threading.Lock()实现:
balance=0 #创建Lock对象 lock=threading.Lock() def run_thread(n): for i in range(100000): #获取锁 lock.acquire() try: change_it(n) #放心修改 finally: lock.release()#释放锁
当多个线程同时通过lock.acquire()请求锁时,只有一个线程可以成功,然后继续执行代码,其他线程继续等待到获得锁为止。
获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将会一直等待下去,成为死线程。所以我们用try...finally保证锁一定会被释放。
锁的好处是确保了某段关键代码只能由一个线程从头到尾完整执行,坏处也很多,①阻止了线程的并发执行,包含锁的某段代码实际上只能以单线程模式执行,降低了效率;②存在多个锁时,线程互相请求对方持有的锁,造成死锁,只能依靠OS强制终止。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性