線程python
多任務能夠由多進程
完成,也能夠由一個進程內的多線程
完成。安全
咱們前面提到了進程是由若干線程
組成的,一個進程
至少有一個線程
。多線程
因爲線程是操做系統直接支持的執行單元,所以,高級語言一般都內置多線程的支持,Python也不例外,而且,Python的線程是真正的Posix Thread,而不是模擬出來的線程。函數
Python的標準庫提供了兩個模塊:thread和threading,thread是低級模塊,threading是高級模塊,對thread進行了封裝。絕大多數狀況下,咱們只須要使用threading
這個高級模塊ui
函數方式
建立線程:操作系統
import threading import time def func1(): i = 0 while i<5 : print('eating......') time.sleep(1) i+=1 def func2(): i = 0 while i<5 : print('running......') time.sleep(1) i+=1 if __name__ == '__main__': t1 = threading.Thread(target=func1) t2 = threading.Thread(target=func2) t1.start() #開始線程 t2.start() t1.join() t2.join() >>> eating...... running...... eating...... running...... eating...... running......
類的方式:線程
import threading,time class Mythread(threading.Thread): def __init__(self,*args,**kwargs): name = kwargs.pop('args') self._heroname = name super(Mythread, self).__init__(*args,**kwargs) def run(self): print('currentThread is %s'%threading.currentThread()) print('hero name %s'%self._heroname) print(self.name) t1 = Mythread(args=('魯班七號')) t2 = Mythread(args='趙雲') t3 = Mythread(args='後裔') t1.start() t2.start() t3.start() >>> currentThread is <Mythread(Thread-1, started 3924)> hero name 魯班七號 Thread-1 currentThread is <Mythread(Thread-2, started 11416)> hero name 趙雲 Thread-2 currentThread is <Mythread(Thread-3, started 5868)> hero name 後裔 Thread-3
多線程共享全局變量code
多線程中,全部變量都由全部線程共享,因此,任何一個變量均可以被任何一個線程修改,所以,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂了
數據混亂:對象
import threading g_nums = 0 def test1(): global g_nums for x in range(1000000): g_nums+=1 def test2(): global g_nums for x in range(1000000): g_nums+=1 if __name__ == '__main__': t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() t1.join() t2.join() print(g_nums) >>> 1320177
互斥鎖遞歸
當線程同時修改末一個共享數據
時,須要進行同步控制。
線程同步可以保證多個線程安全訪問競爭資源,最簡單的同步機制是引入互斥鎖
保證共享數據操做的完整性。每一個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。
import threading from threading import Lock lock = Lock() g_nums = 0 def test1(): global g_nums lock.acquire() for x in range(1000000): g_nums+=1 lock.release() def test2(): global g_nums lock.acquire() for x in range(1000000): g_nums+=1 lock.release() if __name__ == '__main__': t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() t1.join() t2.join() print(g_nums) >>> 2000000
RLock(可重入鎖)
是一個能夠被同一個線程請求屢次的同步指令。RLock使用了「擁有的線程」和「遞歸等級」的概念,處於鎖定狀態時,RLock
被某個線程擁有。擁有RLock
的線程能夠再次調用acquire()
,釋放鎖時須要調用release()
相同次數。
能夠認爲RLock包含一個鎖定池和一個初始值爲0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,爲0時鎖處於未鎖定狀態。
簡言之:Lock屬於全局,Rlock屬於線程
Lock對比Rlock #coding:utf-8 import threading lock = threading.Lock() #Lock對象 lock.acquire() lock.acquire() #產生了死鎖。 lock.release() lock.release() print lock.acquire() import threading rLock = threading.RLock() #RLock對象 rLock.acquire() rLock.acquire() #在同一線程內,程序不會堵塞。 rLock.release() rLock.release()
Condition(條件變量)一般與一個鎖關聯。須要在多個Contidion中共享一個鎖時,能夠傳遞一個Lock/RLock實例給構造方法,不然它將本身生成一個RLock實例。
能夠認爲,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處於等待阻塞狀態,直到另外一個線程調用notify()/notifyAll()通知;獲得通知後線程進入鎖定池等待鎖定。
構造方法:
Condition([lock/rlock])
實例方法:
acquire([timeout])/release():
調用關聯的鎖的相應方法。
wait([timeout]):
調用這個方法將使線程進入Condition的等待池等待通知,並釋放鎖。使用前線程必須已得到鎖定,不然將拋出異常。
notify():
調用這個方法將從等待池挑選一個線程並通知,收到通知的線程將自動調用acquire()嘗試得到鎖定(進入鎖定池);其餘線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。
notifyAll():
調用這個方法將通知等待池中全部的線程,這些線程都將進入鎖定池嘗試得到鎖定。調用這個方法不會釋放鎖定。使用前線程必須已得到鎖定,不然將拋出異常。
例子1:生產者消費者:
# encoding: UTF-8 import threading import time # 商品 product = None # 條件變量 con = threading.Condition() # 生產者方法 def produce(): global product if con.acquire(): while True: if product is None: print( 'produce...') product = 'anything' # 通知消費者,商品已經生產 con.notify() # 等待通知 con.wait() time.sleep(2) # 消費者方法 def consume(): global product if con.acquire(): while True: if product is not None: print( 'consume...') product = None # 通知生產者,商品已經沒了 con.notify() # 等待通知 con.wait() time.sleep(2) t1 = threading.Thread(target=produce) t2 = threading.Thread(target=consume) t2.start() t1.start() >>> produce... consume... produce... consume... produce... consume... ......
例子2:生產者消費者
import threading import time condition = threading.Condition() products = 0 class Producer(threading.Thread): def run(self): global products while True: if condition.acquire(): if products < 10: products += 1 print( "Producer(%s):deliver one, now products:%s" %(self.name, products)) condition.notify()#不釋放鎖定,所以須要下面一句 condition.release() else: print( "Producer(%s):already 10, stop deliver, now products:%s" %(self.name, products)) condition.wait()#自動釋放鎖定 time.sleep(2) class Consumer(threading.Thread): def run(self): global products while True: if condition.acquire(): if products > 1: products -= 1 print( "Consumer(%s):consume one, now products:%s" %(self.name, products)) condition.notify() condition.release() else: print( "Consumer(%s):only 1, stop consume, products:%s" %(self.name, products)) condition.wait() time.sleep(2) if __name__ == "__main__": for p in range(0, 2): p = Producer() p.start() for c in range(0, 3): c = Consumer() c.start() >>> Producer(Thread-1):deliver one, now products:1 Producer(Thread-2):deliver one, now products:2 Consumer(Thread-3):consume one, now products:1 Consumer(Thread-4):only 1, stop consume, products:1 Consumer(Thread-5):only 1, stop consume, products:1 Producer(Thread-2):deliver one, now products:2 ...
Event事件
Event(事件)
是最簡單的線程通訊機制之一:一個線程通知事件,其餘線程等待事件。Event內置了一個初始爲False的標誌,當調用set()
時設爲True
,調用clear()
時重置爲False
。wait()
將阻塞線程至等待阻塞狀態。
Event其實就是一個簡化版的Condition
。Event沒有鎖,沒法使線程進入同步阻塞狀態。
構造方法:
Event()
實例方法:
1.isSet():
當內置標誌爲True時返回True。
2.set():
將標誌設爲True,並通知全部處於等待阻塞狀態的線程恢復運行狀態。
clear():
將標誌設爲False。wait([timeout]):
若是標誌爲True將當即返回,不然阻塞線程至等待阻塞狀態,等待其餘線程調用set()。# encoding: UTF-8 import threading import time event = threading.Event() def func(): # 等待事件,進入等待阻塞狀態 print( '%s wait for event...' % threading.currentThread().getName()) event.wait() # 收到事件後進入運行狀態 print( '%s recv event.' % threading.currentThread().getName()) t1 = threading.Thread(target=func) t2 = threading.Thread(target=func) t1.start() t2.start() time.sleep(2) # 發送事件通知 print( 'MainThread set event.') event.set() >>> Thread-1 wait for event... Thread-2 wait for event... MainThread set event. Thread-2 recv event. Thread-1 recv event.
Event對象關鍵的特性是他會喚醒全部的線程。若是咱們編寫的程序只但願喚醒一個單獨的線程,那麼最好使用Semaphore或者Condition對象
#encoding:utf-8 # __author__ = 'donghao' # __time__ = 2019/4/1 21:30 import threading import time def worker(n, seam): seam.acquire() print('working',n) time.sleep(1) if __name__ == '__main__': seam = threading.Semaphore(0) nworkers = 10 for n in range(10): t = threading.Thread(target=worker, args=(n, seam,)) t.start() while True: time.sleep(1) seam.release() >>> working 0 working 1 working 2 working 3 working 4 working 5 working 6 ......
timer類
Timer(定時器)是Thread的派生類,用於在指定時間後調用一個方法。
構造方法:
Timer(interval, function, args=[], kwargs={})
# encoding: UTF-8 import threading def func(): print( 'hello timer!') timer = threading.Timer(5, func) timer.start()
local類
local是一個小寫字母開頭的類,用於管理 thread-local(線程局部的)數據。對於同一個local,線程沒法訪問其餘線程設置的屬性;線程設置的屬性不會被其餘線程設置的同名屬性替換。
能夠把local當作是一個「線程-屬性字典」的字典,local封裝了從自身使用線程做爲 key檢索對應的屬性字典、再使用屬性名做爲key檢索屬性值的細節。
`# encoding: UTF-8 import threading local = threading.local() local.tname = '王者榮耀' def func(): local.tname = '魯班七號' print(local.tname) t1 = threading.Thread(target=func) t1.start() t1.join() print(local.tname) >>> 魯班七號 王者榮耀