Python线程同步的实现代码
本文介绍Python中的线程同步对象,主要涉及thread和threading模块。
threading模块提供的线程同步原语包括:Lock、RLock、Condition、Event、Semaphore等对象。
线程执行
join与setDaemon
子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束;
如果join()线程,那么主线程会等待子线程执行完再执行。
importthreading importtime defget_thread_a(): print("getthreadAstarted") time.sleep(3) print("getthreadAend") defget_thread_b(): print("getthreadBstarted") time.sleep(5) print("getthreadBend") if__name__=="__main__": thread_a=threading.Thread(target=get_thread_a) thread_b=threading.Thread(target=get_thread_b) start_time=time.time() thread_b.setDaemon(True) thread_a.start() thread_b.start() thread_a.join() end_time=time.time() print("executiontime:{}".format(end_time-start_time))
thread_a是join,首先子线程thread_a执行,thread_b是守护线程,当主线程执行完后,thread_b不会再执行执行结果如下:
getthreadAstarted
getthreadBstarted
getthreadAend
executiontime:3.003199815750122
线程同步
当线程间共享全局变量,多个线程对该变量执行不同的操作时,该变量最终的结果可能是不确定的(每次线程执行后的结果不同),如:对count变量执行加减操作,count的值是不确定的,要想count的值是一个确定的需对线程执行的代码段加锁。
python对线程加锁主要有Lock和Rlock模块
Lock:
fromthreadingimportLock lock=Lock() lock.acquire() lock.release()
Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁
Rlock:
鉴于Lock可能会造成死锁的情况,RLock(可重入锁)对Lock进行了改进,RLock可以在同一个线程里面连续调用多次acquire(),但必须再执行相同次数的release()
fromthreadingimportRLock lock=RLock() lock.acquire() lock.acquire() lock.release() lock.release()
condition(条件变量),线程在执行时,当满足了特定的条件后,才可以访问相关的数据
importthreading defget_thread_a(condition): withcondition: condition.wait() print("A:HelloB,that'sok") condition.notify() condition.wait() print("A:I'mfine,andyou?") condition.notify() condition.wait() print("A:Nicetomeetyou") condition.notify() condition.wait() print("A:That'sallfortoday") condition.notify() defget_thread_b(condition): withcondition: print("B:HiA,Let'sstarttheconversation") condition.notify() condition.wait() print("B:Howareyou") condition.notify() condition.wait() print("B:I'mfinetoo") condition.notify() condition.wait() print("B:Nicetomeetyou,too") condition.notify() condition.wait() print("B:Oh,goodbye") if__name__=="__main__": condition=threading.Condition() thread_a=threading.Thread(target=get_thread_a,args=(condition,)) thread_b=threading.Thread(target=get_thread_b,args=(condition,)) thread_a.start() thread_b.start()
Condition内部有一把锁,默认是RLock,在调用wait()和notify()之前必须先调用acquire()获取这个锁,才能继续执行;当wait()和notify()执行完后,需调用release()释放这个锁,在执行withcondition时,会先执行acquire(),with结束时,执行了release();所以condition有两层锁,最底层锁在调用wait()时会释放,同时会加一把锁到等待队列,等待notify()唤醒释放锁
wait():允许等待某个条件变量的通知,notify()可唤醒
notify():唤醒等待队列wait()
执行结果:
B:HiA,Let'sstarttheconversation
A:HelloB,that'sok
B:Howareyou
A:I'mfine,andyou?
B:I'mfinetoo
A:Nicetomeetyou
B:Nicetomeetyou,too
A:That'sallfortoday
B:Oh,goodbye
Semaphore(信号量)
用于控制线程的并发数,如爬虫中请求次数过于频繁会被禁止ip,每次控制爬取网页的线程数量可在一定程度上防止ip被禁;文件读写中,控制写线程每次只有一个,读线程可多个。
importtime importthreading defget_thread_a(semaphore,i): time.sleep(1) print("getthread:{}".format(i)) semaphore.release() defget_thread_b(semaphore): foriinrange(10): semaphore.acquire() thread_a=threading.Thread(target=get_thread_a,args=(semaphore,i)) thread_a.start() if__name__=="__main__": semaphore=threading.Semaphore(2) thread_b=threading.Thread(target=get_thread_b,args=(semaphore,)) thread_b.start()
上述示例了每隔1秒并发两个线程执行的情况,当调用一次semaphore.acquire()时,Semaphore的数量就减1,直至Semaphore数量为0时被锁上,当release()后Semaphore数量加1。Semaphore在本质上是调用的Condition,semaphore.acquire()在Semaphore的值为0的条件下会调用Condition.wait(),否则将值减1,semaphore.release()会将Semaphore的值加1,并调用Condition.notify()
Semaphore源码
defacquire(self,blocking=True,timeout=None): ifnotblockingandtimeoutisnotNone: raiseValueError("can'tspecifytimeoutfornon-blockingacquire") rc=False endtime=None withself._cond: whileself._value==0: ifnotblocking: break iftimeoutisnotNone: ifendtimeisNone: endtime=_time()+timeout else: timeout=endtime-_time() iftimeout<=0: break self._cond.wait(timeout) else: self._value-=1 rc=True returnrc defrelease(self): withself._cond: self._value+=1 self._cond.notify()
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。