詳解python多線程、鎖、event事件機(jī)制的簡(jiǎn)單使用
線程和進(jìn)程
1、線程共享創(chuàng)建它的進(jìn)程的地址空間,進(jìn)程有自己的地址空間
2、線程可以訪問進(jìn)程所有的數(shù)據(jù),線程可以相互訪問
3、線程之間的數(shù)據(jù)是獨(dú)立的
4、子進(jìn)程復(fù)制線程的數(shù)據(jù)
5、子進(jìn)程啟動(dòng)后是獨(dú)立的 ,父進(jìn)程只能殺掉子進(jìn)程,而不能進(jìn)行數(shù)據(jù)交換
6、修改線程中的數(shù)據(jù),都是會(huì)影響其他的線程,而對(duì)于進(jìn)程的更改,不會(huì)影響子進(jìn)程
threading.Thread
Thread 是threading模塊中最重要的類之一,可以使用它來創(chuàng)建線程。有兩種方式來創(chuàng)建線程:一種是通過繼承Thread類,重寫它的run方法;另一種是創(chuàng)建一個(gè)threading.Thread對(duì)象,在它的初始化函數(shù)(__init__)中將可調(diào)用對(duì)象作為參數(shù)傳入。
先來看看通過繼承threading.Thread類來創(chuàng)建線程的例子:
import threading import time class MyThread(threading.Thread): def __init__(self, arg): # super(MyThread, self).__init__() # 新式類繼承原有方法寫法 threading.Thread.__init__(self) self.arg = arg def run(self): time.sleep(2) print(self.arg) for i in range(10): thread = MyThread(i) print(thread.name) thread.start()
另外一種創(chuàng)建線程的方法:
import threading import time def process(arg): time.sleep(2) print(arg) for i in range(10): t = threading.Thread(target=process, args=(i,)) print(t.name) t.start()
Thread類還定義了以下常用方法與屬性:
Thread.getName() 獲取線程名稱
Thread.setName() 設(shè)置線程名稱
Thread.name 線程名稱
Thread.ident 獲取線程的標(biāo)識(shí)符。線程標(biāo)識(shí)符是一個(gè)非零整數(shù),只有在調(diào)用了start()方法之后該屬性才有效,否則它只返回None
判斷線程是否是激活的(alive)。從調(diào)用start()方法啟動(dòng)線程,到run()方法執(zhí)行完畢或遇到未處理異常而中斷 這段時(shí)間內(nèi),線程是激活的
Thread.is_alive()
Thread.isAlive()
Thread.join([timeout]) 調(diào)用Thread.join將會(huì)使主調(diào)線程堵塞,直到被調(diào)用線程運(yùn)行結(jié)束或超時(shí)。參數(shù)timeout是一個(gè)數(shù)值類型,表示超時(shí)時(shí)間,如果未提供該參數(shù),那么主調(diào)線程將一直堵塞到被調(diào)線程結(jié)束
Python GIL(Global Interpreter Lock)
GIL并不是Python的特性,它是在實(shí)現(xiàn)Python解析器(CPython)時(shí)所引入的一個(gè)概念。就好比C++是一套語(yǔ)言(語(yǔ)法)標(biāo)準(zhǔn),但是可以用不同的編譯器來編譯成可執(zhí)行代碼。有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也一樣,同樣一段代碼可以通過CPython,PyPy,Psyco等不同的Python執(zhí)行環(huán)境來執(zhí)行。像其中的JPython就沒有GIL。然而因?yàn)镃Python是大部分環(huán)境下默認(rèn)的Python執(zhí)行環(huán)境。所以在很多人的概念里CPython就是Python,也就想當(dāng)然的把GIL歸結(jié)為Python語(yǔ)言的缺陷。所以這里要先明確一點(diǎn):GIL并不是Python的特性,Python完全可以不依賴于GIL。
線程鎖的使用:
# 鎖:GIL 全局解釋器 它是為了保證線程在運(yùn)行過程中不被搶占 number = 0 lock = threading.RLock() # 創(chuàng)建鎖 def run(num): lock.acquire() # 加鎖 global number number += 1 print(number) time.sleep(2) lock.release() # 釋放鎖 for i in range(10): t = threading.Thread(target=run, args=(i, )) t.start()
Join & Daemon
主線程A中,創(chuàng)建了子線程B,并且在主線程A中調(diào)用了B.setDaemon(),這個(gè)的意思是,把主線程A設(shè)置為守護(hù)線程,這時(shí)候,要是主線程A執(zhí)行結(jié)束了,就不管子線程B是否完成,一并和主線程A退出.這就是setDaemon方法的含義,這基本和join是相反的。此外,還有個(gè)要特別注意的:必須在start() 方法調(diào)用之前設(shè)置,如果不設(shè)置為守護(hù)線程,程序會(huì)被無限掛起。
class MyThread1(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): print("thread start") time.sleep(3) print('thread end') print('main start') thread1 = MyThread1() # thread1.setDaemon(True) # 設(shè)置子線程是否跟隨主線程一起結(jié)束 thread1.start() time.sleep(1) print('satrt join') # thread1.join() # 使主線程阻塞,直至子線程運(yùn)行完畢再繼續(xù)主線程 print('end join')
def run(n): print('[%s]------running----\n' % n) time.sleep(2) print('--done--') def main(): for i in range(5): t = threading.Thread(target=run, args=[i,]) t.start() # t.join() print('starting thread', t.getName()) m = threading.Thread(target=main,args=[]) # m.setDaemon(True) # 將主線程設(shè)置為Daemon線程,它退出時(shí),其它子線程會(huì)同時(shí)退出,不管是否執(zhí)行完任務(wù) m.start() # m.join() # 使主線程阻塞,直至子線程運(yùn)行完畢再繼續(xù)主線程 print("---main thread done----")
線程鎖(互斥鎖Mutex)
一個(gè)進(jìn)程下可以啟動(dòng)多個(gè)線程,多個(gè)線程共享父進(jìn)程的內(nèi)存空間,也就意味著每個(gè)線程可以訪問同一份數(shù)據(jù),此時(shí),如果2個(gè)線程同時(shí)要修改同一份數(shù)據(jù),會(huì)出現(xiàn)什么狀況?
num = 100 # 設(shè)定一個(gè)共享變量 def subNum(): global num # 在每個(gè)線程中都獲取這個(gè)全局變量 print('--get num:', num) time.sleep(2) num -= 1 # 對(duì)此公共變量進(jìn)行-1操作 thread_list = [] for i in range(100): t = threading.Thread(target=subNum) t.start() thread_list.append(t) for t in thread_list: # 等待所有線程執(zhí)行完畢 t.join() print('final num:', num)
# 加鎖版本 def subNum(): global num # 在每個(gè)線程中都獲取這個(gè)全局變量 print('--get num:', num) time.sleep(1) lock.acquire() # 修改數(shù)據(jù)前加鎖 num -= 1 # 對(duì)此公共變量進(jìn)行-1操作 lock.release() # 修改后釋放 num = 100 # 設(shè)定一個(gè)共享變量 thread_list = [] lock = threading.Lock() # 生成全局鎖 for i in range(100): t = threading.Thread(target=subNum) t.start() thread_list.append(t) for t in thread_list: # 等待所有線程執(zhí)行完畢 t.join() print('final num:', num)
Rlock與Lock的區(qū)別:
RLock允許在同一線程中被多次acquire。而Lock卻不允許這種情況。否則會(huì)出現(xiàn)死循環(huán),程序不知道解哪一把鎖。注意:如果使用RLock,那么acquire和release必須成對(duì)出現(xiàn),即調(diào)用了n次acquire,必須調(diào)用n次的release才能真正釋放所占用的鎖
Events
Python提供了Event對(duì)象用于線程間通信,它是由線程設(shè)置的信號(hào)標(biāo)志,如果信號(hào)標(biāo)志位真,則其他線程等待直到信號(hào)接觸。
Event對(duì)象實(shí)現(xiàn)了簡(jiǎn)單的線程通信機(jī)制,它提供了設(shè)置信號(hào),清除信號(hào),等待等用于實(shí)現(xiàn)線程間的通信。
event = threading.Event() 創(chuàng)建一個(gè)event
1 設(shè)置信號(hào)
event.set()
使用Event的set()方法可以設(shè)置Event對(duì)象內(nèi)部的信號(hào)標(biāo)志為真。Event對(duì)象提供了isSet()方法來判斷其內(nèi)部信號(hào)標(biāo)志的狀態(tài)。
當(dāng)使用event對(duì)象的set()方法后,isSet()方法返回真
2 清除信號(hào)
event.clear()
使用Event對(duì)象的clear()方法可以清除Event對(duì)象內(nèi)部的信號(hào)標(biāo)志,即將其設(shè)為假,當(dāng)使用Event的clear方法后,isSet()方法返回假
3 等待
event.wait()
Event對(duì)象wait的方法只有在內(nèi)部信號(hào)為真的時(shí)候才會(huì)很快的執(zhí)行并完成返回。當(dāng)Event對(duì)象的內(nèi)部信號(hào)標(biāo)志位假時(shí),
則wait方法一直等待到其為真時(shí)才返回。也就是說必須set新號(hào)標(biāo)志位真
def do(event): print('start') event.wait() print('execute') event_obj = threading.Event() for i in range(10): t = threading.Thread(target=do, args=(event_obj,)) t.start() event_obj.clear() inp = input('輸入內(nèi)容:') if inp == 'true': event_obj.set()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Python多線程同步Lock、RLock、Semaphore、Event實(shí)例
- 分析Python感知線程狀態(tài)的解決方案之Event與信號(hào)量
- Python中使用threading.Event協(xié)調(diào)線程的運(yùn)行詳解
- python基于event實(shí)現(xiàn)線程間通信控制
- python Event事件、進(jìn)程池與線程池、協(xié)程解析
- 詳解Python 多線程 Timer定時(shí)器/延遲執(zhí)行、Event事件
- python多線程之事件Event的使用詳解
- Python多線程編程(八):使用Event實(shí)現(xiàn)線程間通信
- python中Event實(shí)現(xiàn)線程間同步介紹
相關(guān)文章
python修改linux中文件(文件夾)的權(quán)限屬性操作
這篇文章主要介紹了python修改linux中文件(文件夾)的權(quán)限屬性操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03詳解Python中使用base64模塊來處理base64編碼的方法
8bit的bytecode經(jīng)常會(huì)被用base64編碼格式保存,Python中自帶base64模塊對(duì)base64提供支持,這里我們就來詳解Python中使用base64模塊來處理base64編碼的方法,需要的朋友可以參考下2016-07-07python實(shí)現(xiàn)快速排序的示例(二分法思想)
本篇文章主要介紹了python實(shí)現(xiàn)快速排序的示例(二分法思想),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03Python實(shí)現(xiàn)嵌套列表的7中方法總結(jié)
這篇文章主要來給大家講解一個(gè)Python的進(jìn)階知識(shí)點(diǎn):如何將一個(gè)嵌套的大列表展開形成一個(gè)列表。小編提供了7種方法供大家學(xué)習(xí)參考,希望大家能喜歡2023-03-03python接口自動(dòng)化之ConfigParser配置文件的使用詳解
這篇文章主要介紹了python接口自動(dòng)化之ConfigParser配置文件的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08