本文介紹Python中的線程同步對象,主要涉及 thread 和 threading 模塊。
threading 模塊提供的線程同步原語包括:Lock、RLock、Condition、Event、Semaphore等對象。
線程執行
join與setDaemon
子線程在主線程運行結束后,會繼續執行完,如果給子線程設置為守護線程(setDaemon=True),主線程運行結束子線程即結束;
如果join()線程,那么主線程會等待子線程執行完再執行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import threading import time def get_thread_a(): print ( "get thread A started" ) time.sleep( 3 ) print ( "get thread A end" ) def get_thread_b(): print ( "get thread B started" ) time.sleep( 5 ) print ( "get thread B end" ) 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 ( "execution time: {}" . format (end_time - start_time)) |
thread_a是join,首先子線程thread_a執行,thread_b是守護線程,當主線程執行完后,thread_b不會再執行執行結果如下:
get thread A started
get thread B started
get thread A end
execution time: 3.003199815750122
線程同步
當線程間共享全局變量,多個線程對該變量執行不同的操作時,該變量最終的結果可能是不確定的(每次線程執行后的結果不同),如:對count變量執行加減操作 ,count的值是不確定的,要想count的值是一個確定的需對線程執行的代碼段加鎖。
python對線程加鎖主要有Lock和Rlock模塊
Lock:
1
2
3
4
|
from threading import Lock lock = Lock() lock.acquire() lock.release() |
Lock有acquire()和release()方法,這兩個方法必須是成對出現的,acquire()后面必須release()后才能再acquire(),否則會造成死鎖
Rlock:
鑒于Lock可能會造成死鎖的情況,RLock(可重入鎖)對Lock進行了改進,RLock可以在同一個線程里面連續調用多次acquire(),但必須再執行相同次數的release()
1
2
3
4
5
6
|
from threading import RLock lock = RLock() lock.acquire() lock.acquire() lock.release() lock.release() |
condition(條件變量),線程在執行時,當滿足了特定的條件后,才可以訪問相關的數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import threading def get_thread_a(condition): with condition: condition.wait() print ( "A : Hello B,that's ok" ) condition.notify() condition.wait() print ( "A : I'm fine,and you?" ) condition.notify() condition.wait() print ( "A : Nice to meet you" ) condition.notify() condition.wait() print ( "A : That's all for today" ) condition.notify() def get_thread_b(condition): with condition: print ( "B : Hi A, Let's start the conversation" ) condition.notify() condition.wait() print ( "B : How are you" ) condition.notify() condition.wait() print ( "B : I'm fine too" ) condition.notify() condition.wait() print ( "B : Nice to meet you,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()釋放這個鎖,在執行with condition時,會先執行acquire(),with結束時,執行了release();所以condition有兩層鎖,最底層鎖在調用wait()時會釋放,同時會加一把鎖到等待隊列,等待notify()喚醒釋放鎖
wait() :允許等待某個條件變量的通知,notify()可喚醒
notify(): 喚醒等待隊列wait()
執行結果:
B : Hi A, Let's start the conversation
A : Hello B,that's ok
B : How are you
A : I'm fine,and you?
B : I'm fine too
A : Nice to meet you
B : Nice to meet you,too
A : That's all for today
B : Oh,goodbye
Semaphore(信號量)
用于控制線程的并發數,如爬蟲中請求次數過于頻繁會被禁止ip,每次控制爬取網頁的線程數量可在一定程度上防止ip被禁;文件讀寫中,控制寫線程每次只有一個,讀線程可多個。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import time import threading def get_thread_a(semaphore,i): time.sleep( 1 ) print ( "get thread : {}" . format (i)) semaphore.release() def get_thread_b(semaphore): for i in range ( 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源碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
def acquire( self , blocking = True , timeout = None ): if not blocking and timeout is not None : raise ValueError( "can't specify timeout for non-blocking acquire" ) rc = False endtime = None with self ._cond: while self ._value = = 0 : if not blocking: break if timeout is not None : if endtime is None : endtime = _time() + timeout else : timeout = endtime - _time() if timeout < = 0 : break self ._cond.wait(timeout) else : self ._value - = 1 rc = True return rc def release( self ): with self ._cond: self ._value + = 1 self ._cond.notify() |
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:http://www.cnblogs.com/FG123/p/9704158.html