python並發編程之多線程

來源:互聯網
上載者:User

標籤:格式化   pytho   name   closed   coding   format   應用   阻塞   from   

開啟線程的兩種方式:

from threading import Threadimport timedef sayhi(name):    time.sleep(2)    print(‘%s say hello‘%name)if__name__==‘__main__‘:    t=Thread(target=sayhi,args=(‘egon‘,))    t.start()    print(‘主線程‘)
方式一
from threading import Threadimport timeclass Sayhi(Thread):    def__init__(self,name):        supper().__init__()        self.name=name    def run(self):        time.sleep(2):        print(‘%s say hello‘%self.name)if __name__==‘__main__‘:    t=Say(‘egon‘)    t.start()    print(‘主線程‘)
方式二

在這裡我要說明一下他們誰的開啟速度快

from threading import Threadfrom multiprocessing import Processimport osdef work():    print(‘hello‘)if __name__ == ‘__main__‘:    #在主進程下開啟線程    t=Thread(target=work)    t.start()    print(‘主線程/主進程‘)    ‘‘‘    列印結果:    hello    主線程/主進程    ‘‘‘    #在主進程下開啟子進程    t=Process(target=work)    t.start()    print(‘主線程/主進程‘)    ‘‘‘    列印結果:    主線程/主進程    hello

很明顯我們可以看到:線上程裡面會先列印子線程在列印主線程,而在進程裡面會先列印主進程然後列印子進程。(在這裡我想簡單的說一下,就是說你開啟一個進程,你得去重新獲得資源,然而開啟線程的時候,資源已經存在了,不需要去開闢新的資源,所以它的開啟速度就會明顯快了好多)

補充:開啟一個線程,在他之上有一個進程,我們在開啟線程的時候回自動產生一個線程,這個線程就叫做主線程,開啟的線程叫做其他線程(為什麼不叫做子線程呢,就是因為在這裡線程他只是共用資源,他們之間沒有任何的依賴關係)

接下來講一下:同一進程內的線程共用該進程的資料(資源)

from  threading import Threadfrom multiprocessing import Processimport osdef work():    global n    n=0if __name__ == ‘__main__‘:    # n=100    # p=Process(target=work)    # p.start()    # p.join()    # print(‘主‘,n) #毫無疑問子進程p已經將自己的全域的n改成了0,但改的僅僅是它自己的,查看父進程的n仍然為100    n=1    t=Thread(target=work)    t.start()    t.join()    print(‘主‘,n) #查看結果為0,因為同一進程內的線程之間共用進程內的資料

(在這裡我要簡單的說明一下,為什麼同一進程內的線程可以共用該進程的資料,從這個執行個體中我們可以清楚的看到,在進程中,子進程他只是將自己的n改成了0,而父進程的n始終都是100,而對於線程來說,n的結果是0,這就是因為,同一進程內的線程共用該進程的資料)

執行個體:三個任務,一個接收使用者輸入,一個將使用者輸入的內容格式化成大寫,一個將格式化後的結果存入檔案(首先,在這裡面三個任務是同時執行的,)

from threading import Threadmsg_l=[]format_l=[]def talk():    while True:        msg=input(‘>>: ‘).strip()        if not msg:continue        msg_l.append(msg)def format_msg():    while True:        if msg_l:            res=msg_l.pop()            format_l.append(res.upper())def save():    while True:        if format_l:            with open(‘db.txt‘,‘a‘,encoding=‘utf-8‘) as f:                res=format_l.pop()                f.write(‘%s\n‘ %res)if __name__ == ‘__main__‘:    t1=Thread(target=talk)    t2=Thread(target=format_msg)    t3=Thread(target=save)    t1.start()    t2.start()    t3.start()

線程相關的其他方法:

Thread執行個體對象的方法  # isAlive(): 返回線程是否活動的。  # getName(): 返回線程名。  # setName(): 設定線程名。threading模組提供的一些方法:  # threading.currentThread(): 返回當前的線程變數。  # threading.enumerate(): 返回一個包含正在啟動並執行線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。  # threading.activeCount(): 返回正在啟動並執行線程數量,與len(threading.enumerate())有相同的結果。複製代碼
from threading import Threadimport threadingfrom multiprocessing import Processimport osdef work():    import time    time.sleep(3)    print(threading.current_thread().getName())if __name__ == ‘__main__‘:    #在主進程下開啟線程    t=Thread(target=work)    t.start()    print(threading.current_thread().getName())    print(threading.current_thread()) #主線程    print(threading.enumerate()) #連同主線程在內有兩個啟動並執行線程    print(threading.active_count())    print(‘主線程/主進程‘)    ‘‘‘    列印結果:    MainThread    <_MainThread(MainThread, started 140735268892672)>    [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]    主線程/主進程    Thread-1    ‘‘‘

主線程等待子線程結束

複製代碼from threading import Threadimport timedef sayhi(name):    time.sleep(2)    print(‘%s say hello‘ %name)if __name__ == ‘__main__‘:    t=Thread(target=sayhi,args=(‘egon‘,))    t.start()    t.join()    print(‘主線程‘)    print(t.is_alive())    ‘‘‘    egon say hello    主線程    False    ‘‘‘複製代碼

守護線程:

無論是進程還是線程,都是:守護xxx會等待主xxx完畢後被銷毀,

主進程與主線程在什麼情況下才算運行完畢

1.主進程在其代碼結束後就已經算運行完畢了,(守護進程就在此時被回收)。主進程會一直等非守護的子進程都運行玩不後回收子進程的資源,(否則會產生殭屍進程),才會結束

2.主線程在其他非守護線程運行完畢後才算運行完畢(守護線程在此時就被回收)。主線程的結束意味著進程的結束,進程整體的資源都被回收,因而主線程必須在其餘非守護線程都運行完畢後才能結束

from threading import Threadimport timedef sayhi(name):    time.sleep(2)    print(‘%s say hello‘ %name)if __name__ == ‘__main__‘:    t=Thread(target=sayhi,args=(‘egon‘,))    t.setDaemon(True) #必須在t.start()之前設定    t.start()    print(‘主線程‘)    print(t.is_alive())    ‘‘‘    主線程    True
八 同步鎖
三個需要注意的點:#1.分析Lock的同時一定要說明:線程搶的是GIL鎖,拿到執行許可權後才能拿到互斥鎖Lock#2.使用join與加鎖的區別:join是等待所有,即整體串列,而鎖只是鎖住一部分,即部分串列#3. 一定要看本小節最後的GIL與互斥鎖的經典分析

GIL VS Lock

    機智的同學可能會問到這個問題,就是既然你之前說過了,Python已經有一個GIL來保證同一時間只能有一個線程來執行了,為什麼這裡還需要lock? 

 首先我們需要達成共識:鎖的目的是為了保護共用的資料,同一時間只能有一個線程來修改共用的資料

    然後,我們可以得出結論:保護不同的資料就應該加不同的鎖。

 最後,問題就很明朗了,GIL 與Lock是兩把鎖,保護的資料不一樣,前者是解譯器層級的(當然保護的就是解譯器層級的資料,比如記憶體回收的資料),後者是保護使用者自己開發的應用程式的資料,很明顯GIL不負責這件事,只能使用者自訂加鎖處理,即Lock

過程分析:所有線程搶的是GIL鎖,或者說所有線程搶的是執行許可權

  線程1搶到GIL鎖,拿到執行許可權,開始執行,然後加了一把Lock,還沒有執行完畢,即線程1還未釋放Lock,有可能線程2搶到GIL鎖,開始執行,執行過程中發現Lock還沒有被線程1釋放,於是線程2進入阻塞,被奪走執行許可權,有可能線程1拿到GIL,然後正常執行到釋放Lock。。。這就導致了串列啟動並執行效果

  既然是串列,那我們執行

  t1.start()

  t1.join

  t2.start()

  t2.join()

  這也是串列執行啊,為何還要加Lock呢,需知join是等待t1所有的代碼執行完,相當於鎖住了t1的所有代碼,而Lock只是鎖住一部分操作共用資料的代碼。

python並發編程之多線程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.