Python學習——Python線程
一、線程創建
1 #方法一:將要執行的方法作為參數傳給Thread的構造方法 2 import threading 3 import time 4 5 def show(arg): 6 time.sleep(2) 7 print(‘thread‘ + str(arg)) 8 9 for i in range(10): 10 t = threading.Thread(target=show,args=(i,)) 11 time.sleep(2) 12 t.start() 13 14 #方法2:從Thread繼承,並重寫run() 15class MyThread(threading.Thread): 16 def __init__(self,num): 17 threading.Thread.__init__(self) 18 self.num = num 19 20 def run(self)):#定義每個線程要運行的函數 21 print("running on number:%s" %self.num) 22 time.sleep(3) 23 24 25 if __name__ == ‘__main__‘: 26 t1 = MyThread(1)27 t2 = MyThread(2) 28 t1.start() 29 time.sleep(3) 30 t2.start()
註解:
Thread(group=None,target=None,name=None,args=(),kwargs={})
group:線程組,目前還沒有實現,庫引用時提示必須是None
target:要執行的方法
name:線程名
args/kwargs:要傳入方法的參數,args和kwargs兩個參數其實是二選一
#實例方法
isAlive():返回線程是否在運行
get/setName(name):獲取/設置線程名
is/setDaemon(bool):獲取/設置是否守護線程。初始值從創建該線程的線程繼承,當沒有非守護線程仍在運行時,程序將終止
start():啟動線程
join([timeout]):阻塞當前上下文環境的線程。
二、Python多線程用法
1 import threading 2 from time import ctime,sleep 3 4 def music(func): 5 for i in range(2): 6 print("I was listening to %s. %s" %(func,ctime())) 7 sleep(1) 8 def move(func): 9 for i in range(2): 10 print("I was at the %s! %s" %(func,ctime())) 11 sleep(5) 12 13 threads = [] 14 t1 = threading.Thread(target=music,args=(‘童話鎮‘,)) 15 threads.append(t1) 16 t2 = threading.Thread(target=move,args=(‘變形金剛‘,)) 17 threads.append(t2) 18 19 if __name__ == ‘__main__‘: 20 for t in threads: 21 t.setDaemon(True) 22 t.start() 23 24 print("all over %s" %ctime())
註:
threads = []
t1 = threading.Thread(target=music,args=(‘童話鎮‘,))
threads.append(t1)
創建了threads數組,創建線程t1,使用threading.Thread()方法,在這個方法中調用music方法target=music,args方法對music進行傳參。 把創建好的線程t1裝到threads數組中。
接著以同樣的方式創建線程t2,並把t2也裝到threads數組。
for t in threads:
t.setDaemon(True)
t.start()
最後通過for循環遍歷數組。(數組被裝載了t1和t2兩個線程)
setDaemon()
setDaemon(True)將線程聲明為守護線程,必須在start() 方法調用之前設置,如果不設置為守護線程程序會被無限掛起。子線程啟動後,父線程也繼續執行下去,當父線程執行完最後一條語句print "all over %s" %ctime()後,沒有等待子線程,直接就退出了,同時子線程也一同結束。
serDeamon(False)(默認)前臺線程,主線程執行過程中,前臺線程也在進行,主線程執行完畢後,等待前臺線程也執行完成後,主線程停止。
運行結果:
I was listening to 童話鎮. Thu Jun 22 23:23:07 2017 I was at the 變形金剛! Thu Jun 22 23:23:07 2017 all over Thu Jun 22 23:23:07 2017
從執行結果來看,子線程(muisc 、move )和主線程(print "all over %s" %ctime())都是同一時間啟動,但由於主線程執行完結束,所以導致子線程也終止。
調整程序:
1 if __name__ == ‘__main__‘: 2 for t in threads: 3 t.setDaemon(True) 4 t.start() 5 6 t.join() 7 8 print "all over %s" %ctime()
加了join()方法,用於等待線程終止。join()的作用是,在子線程完成運行之前,這個子線程的父線程將一直被阻塞。
join()方法的位置是在for循環外的,也就是說必須等待for循環裏的兩個進程都結束後,才去執行主進程。
運行結果:
1 ############運行結果################### 2 I was listening to 童話鎮. Thu Jun 22 23:34:22 2017 3 I was at the 變形金剛! Thu Jun 22 23:34:22 2017 4 I was listening to 童話鎮. Thu Jun 22 23:34:23 2017 5 I was at the 變形金剛! Thu Jun 22 23:34:27 2017 6 all over Thu Jun 22 23:34:32 2017
從結果的時間可以看出每首歌之間等待1秒,電影之間等待5秒,但是是同步進行的,總的時間為5秒
三、線程鎖(LOCK,RLOCK)
由於線程之間是進行隨機調度,並且每個線程可能只執行n條執行之後,當多個線程同時修改同一條數據時可能會出現臟數據,所以,出現了線程鎖 - 同一時刻允許一個線程執行操作。
Lock(指令鎖)是可用的最低級的同步指令。Lock處於鎖定狀態時,不被特定的線程擁有。Lock包含兩種狀態——鎖定和非鎖定,以及兩個基本的方法。
可以認為Lock有一個鎖定池,當線程請求鎖定時,將線程至於池中,直到獲得鎖定後出池。池中的線程處於狀態圖中的同步阻塞狀態。
RLock(可重入鎖)是一個可以被同一個線程請求多次的同步指令。RLock使用了“擁有的線程”和“遞歸等級”的概念,處於鎖定狀態時,RLock被某個線程擁有。擁有RLock的線程可以再次調用acquire(),釋放鎖時需要調用release()相同次數。
可以認為RLock包含一個鎖定池和一個初始值為0的計數器,每次成功調用 acquire()/release(),計數器將+1/-1,為0時鎖處於未鎖定狀態。
簡言之:Lock屬於全局,Rlock屬於線程。
1,未使用鎖
1 import threading 2 import time 3 4 num = 0 5 6 def show(arg): 7 global num 8 time.sleep(1) 9 num +=1 10 print(num) 11 12 for i in range(10): 13 t = threading.Thread(target=show, args=(i,)) 14 t.start() 15 16 print(‘main thread stop‘)
多次運行可能產生混亂。這種場景就是適合使用鎖的場景。
2.使用鎖
1 import threading 2 import time 3 4 num = 0 5 lock = threading.RLock() 6 7 # 調用acquire([timeout])時,線程將一直阻塞, 8 # 直到獲得鎖定或者直到timeout秒後(timeout參數可選)。 9 # 返回是否獲得鎖。 10 def show(arg): 11 lock.acquire() 12 global num 13 time.sleep(1) 14 num +=1 15 print(num) 16 lock.release() 17 18 for i in range(10): 19 t = threading.Thread(target=show, args=(i,)) 20 t.start() 21 22 print(‘main thread stop‘)
加上鎖後數字會一步步打印出來,不會因為擁堵而錯亂的情況!
四、信號量(Semaphore)
互斥鎖 同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據 ,比如廁所有3個坑,那最多只允許3個人上廁所,後面的人只能等裏面有人出來了才能再進去。
1 import threading, time 2 3 def run(n): 4 semaphore.acquire() 5 time.sleep(3) 6 print("run the thread: %s" % n) 7 semaphore.release() 8 9 if __name__ == ‘__main__‘: 10 num = 0 11 semaphore = threading.BoundedSemaphore(5) # 最多允許5個線程同時運行 12 for i in range(20): 13 t = threading.Thread(target=run, args=(i,)) 14 t.start()
五、事件(event)
Python線程的事件主要用於主線程控制其他線程的執行,事件主要提供了三個方法:set、wait、clear
事件處理的機制:全局定義了一個“Flag”,如果“Flag”值為 False,那麽當程序執行 event.wait 方法時就會阻塞,如果“Flag”值為True,那麽event.wait 方法時便不再阻塞。
clear:將“Flag”設置為False
set:將“Flag”設置為True
用threading.Event實現線程間的通訊
threading.Event使一個線程等待其他線程的通知,把這個Event傳遞到線程對象中,Event默認內置了一個標誌,初始值為False。
一旦該線程通過wait()方法進入等待狀態,直到另一個線程調用該Event的set()方法將內置標誌設置為True時,
該Event會通知所有等待狀態的線程恢復運行。
1 import threading 2 3 def do(event): 4 print(‘start‘) 5 event.wait() 6 print(‘end‘) 7 8 event_obj = threading.Event() 9 10 for i in range(10): 11 t = threading.Thread(target=do, args=(event_obj,)) 12 t.start() 13 14 event_obj.clear() #繼續阻塞 15 16 inp = input(‘input:‘) 17 if inp == ‘true‘: 18 event_obj.set() # 喚醒
六、條件(condition)
所謂條件變量,即這種機制是在滿足特定條件之後,線程才可以訪問相關的數據!
它使用Condition類來完成,由於它也可以像鎖機制那樣用,所以它也有acquire方法和release方法,而且它還有wait,notify,notifyAll方法
一個簡單的生產消費者模型,通過條件變量的控制產品數量的增減,調用一次生產者產品就是+1,調用一次消費者產品就會-1.
使用 Condition 類來完成,由於它也可以像鎖機制那樣用,所以它也有 acquire 方法和 release 方法,而且它還有
wait, notify, notifyAll 方法。
1 import threading 2 import time 3 4 # 產品類 5 class Goods: 6 def __init__(self): 7 self.count = 0 8 9 def add(self, num=1): 10 self.count += num 11 12 def sub(self): 13 if self.count >= 0: 14 self.count -= 1 15 16 def empty(self): 17 return self.count <= 0 18 19 # 生產者 20 class Producer(threading.Thread): 21 def __init__(self, condition, goods, sleeptime=1): 22 threading.Thread.__init__(self) 23 self.cond = condition 24 self.goods = goods 25 self.sleeptime = sleeptime 26 27 def run(self): 28 cond = self.cond 29 goods = self.goods 30 while True: 31 # 鎖住資源 32 cond.acquire() 33 goods.add() 34 print("產品數量:", goods.count, "生產者線程") 35 # 喚醒所有等待的線程 -> 其實就是喚醒消費者進程 36 cond.notifyAll() 37 # 解鎖資源 38 cond.release() 39 time.sleep(self.sleeptime) 40 41 42 # 消費者 43 class Consumer(threading.Thread): 44 def __init__(self, condition, goods, sleeptime=2): 45 threading.Thread.__init__(self) 46 self.cond = condition 47 self.goods = goods 48 self.sleeptime = sleeptime 49 50 def run(self): 51 cond = self.cond 52 goods = self.goods 53 54 while True: 55 time.sleep(self.sleeptime) 56 # 鎖住資源 57 cond.acquire() 58 # 如無產品則讓線程等待 59 while goods.empty(): 60 cond.wait() 61 goods.sub() 62 print("產品數量:", goods.count, "消費者線程") 63 64 65 g = Goods() 66 c = threading.Condition() 67 pro = Producer(c, g) 68 pro.start() 69 con = Consumer(c, g) 70 con.start()
七、定時器(Timer)
1 import threading 2 def SayHello(): 3 print("hello world!") 4 t=threading.Timer(3,SayHello) 5 t.start() 6 def other_func(): 7 print("let me running!") 8 t=threading.Timer(1,other_func) 9 t.start() 10 11 if __name__ == "__main__": 12 SayHello() 13 other_func()
Python學習——Python線程