摘要:多線程和鎖作者博客進程和線程進程是執(zhí)行中的計算機程序。線程包括開始執(zhí)行順序和結(jié)束三部分。的多進程相關(guān)模塊模塊是高級別的多線程模塊。線程鎖當(dāng)多線程爭奪鎖時,允許第一個獲得鎖的線程進入臨街區(qū),并執(zhí)行代碼。
Python 多線程和鎖
作者博客:http://zzir.cn/
進程和線程進程是執(zhí)行中的計算機程序。每個進程都擁有自己的地址空間、內(nèi)存、數(shù)據(jù)棧及其它的輔助數(shù)據(jù)。操作系統(tǒng)管理著所有的進程,并為這些進程合理分配時間。進程可以通過派生新的進程來執(zhí)行其它任務(wù),不過每個進程都擁有自己的內(nèi)存和數(shù)據(jù)棧等,進程之間的數(shù)據(jù)交換采用 進程間通信(IPC) 方式。
線程在進程之下執(zhí)行,一個進程下可以運行多個線程,它們之間共享相同上下文。線程包括開始、執(zhí)行順序和結(jié)束三部分。它有一個指針,用于記錄當(dāng)前運行的上下文。當(dāng)其它線程執(zhí)行時,它可以被搶占(中斷)和臨時掛起(也稱睡眠) ——這種做法叫做 讓步(yielding)。
一個進程中的各個線程與主進程共享同一片數(shù)據(jù)空間,與獨立進程相比,線程之間信息共享和通信更加容易。線程一般以并發(fā)執(zhí)行,正是由于這種并發(fā)和數(shù)據(jù)共享機制,使多任務(wù)間的協(xié)作成為可能。當(dāng)然,這種共享也并不是沒有風(fēng)險的,如果多個線程訪問同一數(shù)據(jù)空間,由于訪問順序不同,可能導(dǎo)致結(jié)果不一致,這種情況通常稱為競態(tài)條件(race condition),不過大多數(shù)線程庫都有同步原語,以允許線程管理器的控制執(zhí)行和訪問;另一個要注意的問題是,線程無法給予公平執(zhí)行時間,CPU 時間分配會傾向那些阻塞更少的函數(shù)。
全局解釋器鎖(GIL)Python 代碼執(zhí)行由 Python 虛擬機 (又名解釋器主循環(huán)) 進行控制。Python 在設(shè)計時是這樣考慮的,在主循環(huán)中同時只能有一個控制線程在執(zhí)行。對 Python 虛擬機的訪問由 全局解釋器(GIL) 控制,這個鎖用于,當(dāng)有多個線程時保證同一時刻只能有一個線程在運行。
由于 Python 的 GIL 的限制,多線程更適合 I/O 密集型應(yīng)用( I/O 釋放了 GIL,可以允許更多的并發(fā)),對于計算密集型應(yīng)用,為了實現(xiàn)更好的并行性,適合使用多進程,已便利用 CPU 的多核優(yōu)勢。Python 的多進程相關(guān)模塊:subprocess、multiprocessing、concurrent.futures
threading 模塊threading 是 Python 高級別的多線程模塊。
threading 模塊的函數(shù)active_count() 當(dāng)前活動的 Thread 對象個數(shù)
current_thread() 返回當(dāng)前 Thread 對象
get_ident() 返回當(dāng)前線程
enumerater() 返回當(dāng)前活動 Thread 對象列表
main_thread() 返回主 Thread 對象
settrace(func) 為所有線程設(shè)置一個 trace 函數(shù)
setprofile(func) 為所有線程設(shè)置一個 profile 函數(shù)
stack_size([size]) 返回新創(chuàng)建線程棧大?。换驗楹罄m(xù)創(chuàng)建的線程設(shè)定棧大小為 size
TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 允許的最大值
threading 可用對象列表:
Thread 表示執(zhí)行線程的對象
Lock 鎖原語對象
RLock 可重入鎖對象,使單一進程再次獲得已持有的鎖(遞歸鎖)
Condition 條件變量對象,使得一個線程等待另一個線程滿足特定條件,比如改變狀態(tài)或某個值
Semaphore 為線程間共享的有限資源提供一個"計數(shù)器",如果沒有可用資源會被阻塞
Event 條件變量的通用版本,任意數(shù)量的線程等待某個時間的發(fā)生,在改事件發(fā)生后所有線程被激活
Timer 與 Thread 相識,不過它要在運行前等待一段時間
Barrier 創(chuàng)建一個"阻礙",必須達到指定數(shù)量的線程后才可以繼續(xù)
Thread 類Thread 對象的屬性有:Thread.name、Thread.ident、Thread.daemon。詳見(The Python Standard Library)
Thread 對象方法:
Thread.start()、Thread.run()、Thread.join(timeout=None)、Thread.getName、Thread.setName、Thread.is_alive()、Thread.isDaemon()、Thread.setDaemon()。詳見(The Python Standard Library)
使用 Thread 類,可以有很多種方法來創(chuàng)建線程,這里使用常見的兩種:
創(chuàng)建 Thread 實例,傳給它一個函數(shù)。
派生 Thread 子類,并創(chuàng)建子類的實例。
一個單線程栗子#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) # 打印 Sleep 的秒數(shù) def main(): print("### Start at: ", ctime()) for i in range(10): hi(randint(1,2)) # 調(diào)用十次,每次 Sleep 1秒或2秒 print("### Done at: ", ctime()) if __name__ == "__main__": main()
運行結(jié)果:
### Start at: Thu Sep 1 14:11:00 2016 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ### Done at: Thu Sep 1 14:11:14 2016
一共是用了14秒。
多線程:創(chuàng)建 Thread 實例,傳給它一個函數(shù)直接上代碼:
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) # 實例化每個 Thread 對象,把函數(shù)和參數(shù)傳遞進去,返回 Thread 實例 t = threading.Thread(target=hi, args=(rands,)) threads.append(t) # 分配線程 for i in range(10): threads[i].start() # 開始執(zhí)行多線程 for i in range(10): threads[i].join() # (自旋鎖)等待線程結(jié)束或超時,然后再往下執(zhí)行 print("### Done at: ", ctime()) if __name__ == "__main__": main()
運行結(jié)果:
### Start at: Thu Sep 1 14:18:00 2016 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ### Done at: Thu Sep 1 14:18:02 2016
使用多線程,只用了2秒。
多線程:派生 Thread 子類,并創(chuàng)建子類的實例#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime class MyThread(threading.Thread): def __init__(self, func, args, times): super(MyThread, self).__init__() self.func = func self.args = args self.times = times def run(self): print("begin thread......", self.times) self.res = self.func(*self.args) print("end threads......", self.times) def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) t = MyThread(hi, (rands,), i+1) threads.append(t) for i in range(10): threads[i].start() for i in range(10): threads[i].join() print("### Done at: ", ctime()) if __name__ == "__main__": main()
執(zhí)行結(jié)果:
### Start at: Thu Sep 1 14:47:09 2016 begin thread...... 1 begin thread...... 2 begin thread...... 3 begin thread...... 4 begin thread...... 5 begin thread...... 6 begin thread...... 7 begin thread...... 8 begin thread...... 9 begin thread...... 10 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 end threads...... 1 end threads...... 4 ZzZzzz, sleep: 1 end threads...... 7 ZzZzzz, sleep: 1 end threads...... 3 ZzZzzz, sleep: 1 end threads...... 9 ZzZzzz, sleep: 2 end threads...... 2 ZzZzzz, sleep: 2 end threads...... 5 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 end threads...... 10 end threads...... 6 ZzZzzz, sleep: 2 end threads...... 8 ### Done at: Thu Sep 1 14:47:11 2016
這個栗子對 Thread 子類化,而不是對其實例化,使得定制線程對象更具靈活性,同時也簡化線程創(chuàng)建的調(diào)用過程。
線程鎖當(dāng)多線程爭奪鎖時,允許第一個獲得鎖的線程進入臨街區(qū),并執(zhí)行代碼。所有之后到達的線程將被阻塞,直到第一個線程執(zhí)行結(jié)束,退出臨街區(qū),并釋放鎖。需要注意,那些阻塞的線程是沒有順序的。
舉個栗子:
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime L = threading.Lock() # 引入鎖 def hi(n): L.acquire() # 加鎖 for i in [1,2]: print(i) sleep(n) print("ZzZzzz, sleep: ", n) L.release() # 釋放鎖 def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) t = threading.Thread(target=hi, args=(rands,)) threads.append(t) for i in range(10): threads[i].start() for i in range(10): threads[i].join() print("### Done at: ", ctime()) if __name__ == "__main__": main()
運行上面的代碼,再將鎖的代碼注釋掉,對比下輸出。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/38155.html
摘要:可重入意味著鎖被綁定到當(dāng)前線程,線程可以安全地多次獲取相同的鎖,而不會發(fā)生死鎖例如同步方法在同一對象上調(diào)用另一個同步方法。寫入鎖釋放后,兩個任務(wù)并行執(zhí)行,它們不必等待對方是否完成,因為只要沒有線程持有寫入鎖,它們就可以同時持有讀取鎖。 原文地址: Java 8 Concurrency Tutorial: Synchronization and Locks 為了簡單起見,本教程的示例代...
摘要:在接下來的分鐘,你將會學(xué)會如何通過同步關(guān)鍵字,鎖和信號量來同步訪問共享可變變量。所以在使用樂觀鎖時,你需要每次在訪問任何共享可變變量之后都要檢查鎖,來確保讀鎖仍然有效。 原文:Java 8 Concurrency Tutorial: Synchronization and Locks譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 歡迎閱讀我的Java8并發(fā)教程的第二部分。這份指南將...
摘要:如果某線程并未使用很多操作,它會在自己的時間片內(nèi)一直占用處理器和。在中使用線程在和等大多數(shù)類系統(tǒng)上運行時,支持多線程編程。守護線程另一個避免使用模塊的原因是,它不支持守護線程。 這一篇是Python并發(fā)的第四篇,主要介紹進程和線程的定義,Python線程和全局解釋器鎖以及Python如何使用thread模塊處理并發(fā) 引言&動機 考慮一下這個場景,我們有10000條數(shù)據(jù)需要處理,處理每條...
摘要:上一篇文章進程專題進程池下一篇文章進程專題共享數(shù)據(jù)與同步模塊支持的進程間通信主要有兩種管道和隊列。隊列底層使用管道和鎖,同時運行支持線程講隊列中的數(shù)據(jù)傳輸?shù)降讓庸艿乐?,來實?xí)進程間通信。 上一篇文章:Python進程專題4:進程池Pool下一篇文章:Python進程專題6:共享數(shù)據(jù)與同步 multiprocessing模塊支持的進程間通信主要有兩種:管道和隊列。一般來說,發(fā)送較少的大...
摘要:本文旨在對鎖相關(guān)源碼本文中的源碼來自使用場景進行舉例,為讀者介紹主流鎖的知識點,以及不同的鎖的適用場景。中,關(guān)鍵字和的實現(xiàn)類都是悲觀鎖。自適應(yīng)意味著自旋的時間次數(shù)不再固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。 前言 Java提供了種類豐富的鎖,每種鎖因其特性的不同,在適當(dāng)?shù)膱鼍跋履軌蛘宫F(xiàn)出非常高的效率。本文旨在對鎖相關(guān)源碼(本文中的源碼來自JDK 8)、使用場景...
閱讀 2914·2021-11-24 09:39
閱讀 1723·2021-09-28 09:35
閱讀 1181·2021-09-06 15:02
閱讀 1448·2021-07-25 21:37
閱讀 2833·2019-08-30 15:53
閱讀 3711·2019-08-30 14:07
閱讀 767·2019-08-30 11:07
閱讀 3604·2019-08-29 18:36