1、線程基礎python
一、線程狀態 app
二、線程互斥鎖同步控制 dom
多個線程須要修改同一共享數據時,須要進行同步控制,引入了鎖的概念。函數
a、同一時間可能有多個線程在鎖定池中,它們處於同步阻塞狀態競爭鎖定;post
b、同一時間只能有一個線程得到鎖定處於運行狀態。 ui
三、條件變量(線程通訊) spa
有的線程須要預備條件才能幹活。
2、threading:線程建立、啓動、睡眠、退出操作系統
一、方法一:將要執行的函數做爲參數傳遞給 threading.Thread() 線程
1 # -*- coding:utf-8 -*- 2 import threading, time 3 def func(n): 4 global count 5 time.sleep(0.1) 6 for i in range(n): 7 count += 1 8 if __name__==’__main__’: 9 count = 0 10 threads = [] 11 for i in range(5): 12 threads.append(threading.Thread(target=func, args=(1000,))) 13 for t in threads: 14 t.start() 15 time.sleep(5) 16 print(‘count:’,count)
以上例子建立了 5 個線程去執行 func 函數。得到的結果多是 5000,但也有時候會出現錯誤,解決方法請繼續閱 讀下文。
要點: 3d
threading.Thread(target=func, args=(10,)):func 爲函數名,args 爲函數參數(必須以元組的形式傳遞 );
t.start(): 啓動函數,等待操做系統調度;
c. 函數運行結束,線程也就結束;
d. time.sleep(): 線程進入睡眠,處於 IO 阻塞狀態。
一、方法二:繼承 threading.Thread()類,並重寫 run()
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def __init__(self, n): 5 threading.Thread.__init__(self) 6 self.myThread_n = n 7 def run(self): 8 global count 9 for i in range(self.myThread_n): 10 count += 1 11 if __name__==’__main__’: 12 count = 0 13 threads = [] 14 for i in range(5): 15 threads.append(myThread(1000)) 16 for t in threads: 17 t.start() 18 time.sleep(5) 19 print(‘count:’,count)
要點:
a. threading.Thread.__init__(self):回調父類方法。若是你重寫了__init__()方法,這一步是必須的,不然出錯。
3、threading:線程同步鎖互斥控制
由於線程是亂序執行的,在某種狀況下上面的兩個例子,輸出的結果可能不是預期的值。
我將第 2 例的線程類修改下,讓問題更加突出,而後你每次運行的時候再把線程數也修改下,
並計算出預期結果和運行 結果對比。必定要多試幾回哦。
1 class myThread(threading.Thread): 2 def __init__(self, n): 3 threading.Thread.__init__(self) 4 self.myThread_n = n 5 def run(self): 6 global count 7 for i in range(self.myThread_n): 8 __Temp = count time.sleep(0.0001) 9 count = __Temp + 1
是否是輸出的結果和預期結果不一致啊,呵呵!由於多個線程都在同時操做同一個共享資源
,因此形成資源破壞。不過 咱們能夠經過同步鎖來解決這種問題:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def __init__(self, n): 5 threading.Thread.__init__(self) 6 self.myThread_n = n 7 def run(self): 8 global count 9 for i in range(self.myThread_n): 10 if lock.acquire(): 11 __Temp = count 12 time.sleep(0.0001) 13 count = __Temp + 1 14 lock.release() 15 if __name__==’__main__’: 16 count = 0 17 lock = threading.Lock() #同步鎖 18 threads = [] 19 for i in range(5): 20 21 threads.append(myThread(1000)) for t in threads: 22 t.start() time.sleep(5) 23 print(‘count:’,count)
要點:
lock = threading.Lock():建立鎖;
lock.acquire([timeount]):請求鎖定,若是設定了 timeout,則在超時後經過返回值能夠判斷是否獲得了鎖 ,
從而能夠進行一些其餘的處理 ; c. lock.release():釋放鎖定。
4、threading:線程死鎖和遞歸鎖
一、死鎖
a. 在線程間共享多個資源的時候,若是兩個線程分別佔有一部分資源而且同時等待對方的資源,
就會形成死鎖 , 由於系統判斷這部分資源都正在使用 ,全部這兩個線程在無外力做用下將一直
等待下去 。下面是一個死鎖的例 子:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 class myThread(threading.Thread): 4 def doA(self): 5 if lockA.acquire(): 6 print(self.name,」got lockA.」) time.sleep(0.0001) 7 if lockB.acquire(): 8 print(self.name,」got lockB」) 9 lockB.release() 10 lockA.release() 11 def doB(self): 12 if lockB.acquire(): 13 print(self.name,」got lockB.」) 14 time.sleep(0.0001) 15 if lockA.acquire(): 16 print(self.name,」got lockA」) 17 lockA.release() 18 lockB.release() 19 def run(self): 20 self.doA() 21 self.doB() 22 if __name__==’__main__’: 23 lockA = threading.Lock() 24 lockB = threading.Lock() 25 threads = [] 26 for i in range(5): 27 threads.append(myThread()) 28 for t in threads: 29 t.start() 30 for t in threads: 31 t.join() #等待線程結束,後面再講。
b. 當一個線程已經得到了鎖,再此請求鎖也會出現死鎖,請看下面的例子:
1 # -*- coding:utf-8 -*- 2 import threading, time 3 import random 4 class myThread(threading.Thread): 5 def toHex(self): 6 global L 7 if lock.acquire(1): 8 for i in range(len(L)): 9 if type(L[i]) is int: L[i]=」{:X}」.format(L[i]) 10 lock.release() 11 else: 12 print(self.name,「錯誤,系統忙」) 13 def run(self): 14 global L 15 if lock.acquire(1): 16 L.append(random.randint(0, 15)) 17 self.toHex() 18 lock.release() 19 else: 20 print(self.name,「錯誤,系統忙」) 21 if __name__==’__main__’: 22 L = [] 23 lock = threading.Lock() 24 threads = [] 25 for i in range(5): 26 threads.append(myThread()) 27 for t in threads: 28 t.start() 29 for t in threads: 30 t.join() #等待線程結束,後面再講。
二、遞歸鎖(也稱可重入鎖)
上一例子的死鎖,咱們能夠用遞歸鎖解決:
1 #lock = threading.Lock() 註釋掉此行,並加入下行。 2 lock = threading.RLock()
爲了支持在同一線程中屢次請求同一資源 ,python 提供了「可重入鎖」:threading.RLock。RLock 內部維護着一個 Lock 和一個 counter 變量,counter 記錄了 acquire 的次數,從而使得資源能夠被屢次 require。直到一個線程全部的 acquire 都 被 release,其餘的線程才能得到資源。
遞歸鎖通常應用於複雜的邏輯。
5、threading:條件變量同步
有一類線程須要知足條件以後纔可以繼續執行, Python 提供了 threading.Condition 對象用於條件變量線程的支持,它除 了能提供 RLock()或 Lock()的方法外,還提供了 wait()、notify()、notifyAll()方法。
lock_con = threading.Condition([Lock/Rlock]) :鎖是可選選項,不傳人鎖,對象自動建立一個 RLock()。 wait():條件不知足時調用,線程會釋放鎖並進入等待阻塞; notify():條件創造後調用,通知等待池激活一個線程;
#lock = threading.Lock() 註釋掉此行,並加入下行。 lock = threading.RLock()
notifyAll():條件創造後調用,通知等待池激活全部線程。