1. 程式人生 > >Python學習——Python線程

Python學習——Python線程

運行時 消費者 出現 sum 事件 停止 pytho highlight dea

一、線程創建

 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()
15
class 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線程