Python3 進程 線程 同步鎖 線程死鎖和遞歸鎖


進程是最小的資源單位,線程是最小的執行單位

一、進程                                                                                                                                                       

進程:就是一個程序在一個數據集上的一次動態執行過程。

進程由三部分組成:

1、程序:我們編寫的程序用來描述進程要完成哪些功能以及如何完成

2、數據集:數據集則是程序在執行過程中所需要使用的資源

3、進程控制塊:進程控制塊用來記錄進程的外部特征,描述進程的執行變化過程,系統可以利用它來控制和管理進程,它是系統感

     知進程存在的唯一標志。

二、線程                                                                                                                                                            

   Threading用於提供線程相關的操作。線程是應用程序中工作的最小單元,它被包含在進程之中,是進程中的實際運作單位。一

條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。

 

1、實現線程並發

 示例1:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading #線程  6 import time  7  8 def Hi(num): #有一個參數  9 print("hello %s" %num) 10 time.sleep(3) 11 12 if __name__ == '__main__': 13 14 t1=threading.Thread(target=Hi,args=(10,)) #創建了一個線程對象t1,10做為一個參數,傳給num 15  t1.start() 16 17 t2=threading.Thread(target=Hi,args=(9,)) #創建了一個線程對象t2,9做為一個參數,傳給num 18  t2.start() 19 20 print("ending.........") #主線程輸出ending
復制代碼

執行結果:

1 hello 10    #子線程 2 hello 9 #子線程 3 ending.........  #主線程 4 #上面三個同時出來,再停頓三秒才結束 5 Process finished with exit code 0 #停頓3秒才結束

 

示例2:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def music():  9 print("begin to listen %s"%time.ctime()) 10 time.sleep(3) 11 print("stop to listen %s" %time.ctime()) 12 13 def game(): 14 print("begin to play game %s"%time.ctime()) 15 time.sleep(5) 16 print("stop to play game %s" %time.ctime()) 17 18 if __name__ == '__main__': 19 20 t1=threading.Thread(target=music) 21  t1.start() 22 t2=threading.Thread(target=game) 23 t2.start()
復制代碼

執行結果:

復制代碼
1 #總共花了5秒時間 2 3 begin to listen Sat Jan 14 12:34:43 2017 4 begin to play game Sat Jan 14 12:34:43 2017 #1、先打印2個 5 6 stop to listen Sat Jan 14 12:34:46 2017 #2、等待3秒再打印一個 7 8 stop to play game Sat Jan 14 12:34:48 2017 #3、再等待2秒,打印一個
復制代碼

 

2、使用join方法

示例1:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def music():  9 print("begin to listen %s"%time.ctime()) 10 time.sleep(3) 11 print("stop to listen %s" %time.ctime()) 12 13 def game(): 14 print("begin to play game %s"%time.ctime()) 15 time.sleep(5) 16 print("stop to play game %s" %time.ctime()) 17 18 if __name__ == '__main__': 19 20 t1=threading.Thread(target=music) 21 t2=threading.Thread(target=game) 22 23 t1.start() #運行實例的方法 24  t2.start() 25 26  t1.join() #子線程對象調用join()方法 27  t2.join() 28 29 print("ending") #在主線程中
復制代碼

執行結果:

復制代碼
begin to listen Sat Jan 14 12:58:34 2017
begin to play game Sat Jan 14 12:58:34 2017  #先打印2個  stop to listen Sat Jan 14 12:58:37 2017 #等待3秒,再打印一個  stop to play game Sat Jan 14 12:58:39 2017 #等待2秒,再打印兩個 ending 
復制代碼

 

示例2:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def music():  9 print("begin to listen %s"%time.ctime()) 10 time.sleep(3) 11 print("stop to listen %s" %time.ctime()) 12 13 def game(): 14 print("begin to play game %s"%time.ctime()) 15 time.sleep(5) 16 print("stop to play game %s" %time.ctime()) 17 18 if __name__ == '__main__': 19 20 t1=threading.Thread(target=music) 21 t2=threading.Thread(target=game) 22 23 t1.start() #運行實例的方法 24  t2.start() 25 26 t1.join() #t1線程不結束,誰都不往下走 27 28 print("ending") 
復制代碼

執行結果:

復制代碼
1 begin to listen Sat Jan 14 13:06:07 2017
2 begin to play game Sat Jan 14 13:06:07 2017 #先打印這兩行 3 4 stop to listen Sat Jan 14 13:06:10 2017 #再等待3秒打印這兩行 5 ending 6 7 stop to play game Sat Jan 14 13:06:12 2017 #再等待2秒打印這行
復制代碼

 

示例3:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def music():  9 print("begin to listen %s"%time.ctime()) 10 time.sleep(3) 11 print("stop to listen %s" %time.ctime()) 12 13 def game(): 14 print("begin to play game %s"%time.ctime()) 15 time.sleep(5) 16 print("stop to play game %s" %time.ctime()) 17 18 if __name__ == '__main__': 19 20 t1=threading.Thread(target=music) 21 t2=threading.Thread(target=game) 22 23 t1.start() #運行實例的方法 24  t2.start() 25 26  t2.join() 27 28 print("ending") #在主線程中
復制代碼

執行結果:

復制代碼
1 begin to listen Sat Jan 14 13:12:34 2017     #先打印這兩行 2 begin to play game Sat Jan 14 13:12:34 2017 3 4 stop to listen Sat Jan 14 13:12:37 2017 #等待3秒,打印這一行 5 6 stop to play game Sat Jan 14 13:12:39 2017 #等待2秒,打印這兩行 7 ending
復制代碼

 

示例4:並沒有實現並發(失去多線程的意義)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def music():  9 print("begin to listen %s"%time.ctime()) 10 time.sleep(3) 11 print("stop to listen %s" %time.ctime()) 12 13 def game(): 14 print("begin to play game %s"%time.ctime()) 15 time.sleep(5) 16 print("stop to play game %s" %time.ctime()) 17 18 if __name__ == '__main__': 19 20 t1=threading.Thread(target=music) 21 t2=threading.Thread(target=game) 22 23  t1.start() 24 25  t1.join() 26  t2.start() 27 28  t2.join() 29 30 print("ending") #在主線程中
復制代碼

執行結果:

復制代碼
1 begin to listen Sat Jan 14 13:26:18 2017    #先打印條1行 2 3 stop to listen Sat Jan 14 13:26:21 2017 #等待3秒再打印2行 4 begin to play game Sat Jan 14 13:26:21 2017 5 6 stop to play game Sat Jan 14 13:26:26 2017  #等待5秒打印2行 7 ending
復制代碼

 

三、線程的兩種調用方式                                                                                                                                       

  threading 模塊建立在 thread 模塊之上。thread 模塊以低級、原始的方式來處理和控制線程,而 threading 模塊通過對 thread

進行二次封裝,提供了更方便的 api 來處理線程。

1、直接調用(推薦寫法)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8  9 def sayhi(num): # 定義每個線程要運行的函數 10 11 print("running on number:%s" % num) 12 13 time.sleep(3) 14 15 16 if __name__ == '__main__': 17 t1 = threading.Thread(target=sayhi, args=(1,)) # 生成一個線程實例 18 t2 = threading.Thread(target=sayhi, args=(2,)) # 生成另一個線程實例 19 20 t1.start() # 啟動線程 21 t2.start() # 啟動另一個線程 22 23 print(t1.getName()) # 獲取線程名 24 print(t2.getName())
復制代碼

執行結果:

1 running on number:1
2 running on number:2 3 Thread-1 4 Thread-2

 

2、繼承式調用(有些編程人員會用這種寫法,也要能看懂。不推薦這種寫法)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 #自己定制一個MyThread的類  9 class MyThread(threading.Thread): 10 def __init__(self, num): 11 threading.Thread.__init__(self) 12 self.num = num 13 14 def run(self): # 定義每個線程要運行的函數 15 16 print("running on number:%s" % self.num) 17 18 time.sleep(3) 19 20 21 if __name__ == '__main__': 22 t1 = MyThread(1)  #繼承這個類,把1這個參數,傳給num ,t1就是個線程對象 23 t2 = MyThread(2) 24  t1.start() 25  t2.start() 26 27 print("ending......")
復制代碼

執行結果:

1 running on number:1
2 running on number:2 3 ending......

 

四、 threading.thread的實例方法                                                                                                                   

1、join&Daemon方法

示例1:沒有用Daemon方法示例

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18 sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30 for t in threads: 31  t.start() 32 33 print ("all over %s" %ctime())
復制代碼

執行結果:

復制代碼
1 Begin listening to 水手. Sat Jan 14 13:44:10 2017
2 Begin recording the python線程! Sat Jan 14 13:44:10 2017 3 all over Sat Jan 14 13:44:10 2017 #先打印三個出來; 主線程結束了 4 5 end listening Sat Jan 14 13:44:13 2017 #等待3秒,打印這1個; 子線程還沒有結束,會繼續往下運行 6 7 end recording Sat Jan 14 13:44:15 2017 #再等待2秒,打印這1個
復制代碼

 

示例2: 用Daemon方法示例(設置t為守護線程,就是子線程,跟着主線程一起退出)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18 sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30 for t in threads: 31 t.setDaemon(True) #設置t為守護線程; 注意:一定在start()之前設置,否則會報錯 32 33  t.start() 34 35 print ("all over %s" %ctime())
復制代碼

執行結果:

1 Begin listening to 水手. Sat Jan 14 13:51:30 2017    #三個同時打印出來 2 Begin recording the python線程! Sat Jan 14 13:51:30 2017 3 all over Sat Jan 14 13:51:30 2017

 

示例3:設置t1為守護線程,沒有意義,達不到效果,因為t2還會繼續執行

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18 sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30 t1.setDaemon(True)  #設置t1為守護線程; 注意:一定在start之前設置,否則會報錯 31 for t in threads: 32 33  t.start() 34 35 print ("all over %s" %ctime())
復制代碼

執行結果:

復制代碼
1 Begin listening to 水手. Sat Jan 14 14:02:07 2017
2 Begin recording the python線程! Sat Jan 14 14:02:07 2017 3 all over Sat Jan 14 14:02:07 2017 #設置t1為守護線程,所以會先把這三條先打印出來 4 5 end listening Sat Jan 14 14:02:10 2017  #再等待3秒打印t2, 6 7 end recording Sat Jan 14 14:02:12 2017 #再等待3秒打印這條出來
復制代碼

 

示例4:設置t2為守護線程,子線程才會跟着主線程一起退出

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18  sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30  t2.setDaemon(True) # 設置t2為守護線程; 注意:一定在start之前設置,否則會報錯 31 for t in threads: 32 33  t.start() 34 35 print ("all over %s" %ctime())
復制代碼

執行結果:

1 Begin listening to 水手. Sat Jan 14 14:17:09 2017
2 Begin recording the python線程! Sat Jan 14 14:17:09 2017 3 all over Sat Jan 14 14:17:09 2017 #先打印這三條 4 5 end listening Sat Jan 14 14:17:12 2017 #等待3秒,再打印這條;t1結束后,主線程也結束了。

 

2、一道面試題

復制代碼
 1 #執行結果是什么?
 2 
 3 i = 0
 4 for i in range(10):
 5     i += 1
 6 print(i)
 7 
 8 執行結果:
 9 10
復制代碼

 

3、其它方法

示例:getName()方法 (一般沒什么用)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18 sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30 t2.setDaemon(True) # 設置t為守護進程; 注意:一定在start之前設置,否則會報錯 31 for t in threads: 32  t.start() 33 print(t.getName())  #返回線程名稱:Thread-1 34 35 print ("all over %s" %ctime())
復制代碼

執行結果:

1 Begin listening to 水手. Sat Jan 14 14:36:44 2017
2 Thread-1  #返回線程名稱 3 Begin recording the python線程! Sat Jan 14 14:36:44 2017 4 Thread-2 #返回默認的線程名稱 5 all over Sat Jan 14 14:36:44 2017 6 end listening Sat Jan 14 14:36:47 2017

 

 示例:threading.activeCount(),返回正在運行的線程數量

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 from time import ctime,sleep  7 import time  8  9 def ListenMusic(name): 10 11 print ("Begin listening to %s. %s" %(name,ctime())) 12 sleep(3) 13 print("end listening %s"%ctime()) 14 15 def RecordBlog(title): 16 17 print ("Begin recording the %s! %s" %(title,ctime())) 18 sleep(5) 19 print('end recording %s'%ctime()) 20 21 #創建一個列表,把t1和t2加到列表中去 22 threads = [] 23 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 24 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 25 threads.append(t1) 26 threads.append(t2) 27 28 if __name__ == '__main__': 29 30 t2.setDaemon(True) #設置t為守護進程; 注意:一定在start之前設置,否則會報錯 31 for t in threads: 32  t.start() 33 34 print("count:", threading.active_count()) #判斷有多少個線程的數量 35 36 while threading.active_count()==1: #等於1就相當於只有一個主線程,沒有子線程 37 38 print ("all over %s" %ctime())
復制代碼

執行結果:

1 Begin listening to 水手. Sat Jan 14 14:49:00 2017
2 count: 2 3 Begin recording the python線程! Sat Jan 14 14:49:00 2017 4 count: 3 #得到的線程數量 5 end listening Sat Jan 14 14:49:03 2017

 

五、進程與線程的關系區別                                                                                                                                    

1、 一個程序至少有一個進程,一個進程至少有一個線程.(進程可以理解成線程的容器)
2、 進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
3、 線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和 程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
4、 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調 度的一個獨立單位. 線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程 自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧)但是 它可與同屬一個進程的其他的線程共享進程所擁有的全部資源. 一個線程可以創建和撤銷另一個線程;同一個進程中的多個線程之間可以並發執行.

 

六、python的GIL                                                                                                                                              

復制代碼
  In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once.
This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features havegrown
to depend on the guarantees that it enforces.) 上面的核心意思就是:無論你啟多少個線程,你有多少個cpu, Python在執行的時候會淡定的在同一時刻只允許一個線程運行。
復制代碼

 

常見概念:

1、什么是並發和並行?

並發:是指系統具有處理多個任務(動作)的能力(CPU通過切換來完成並發),並發是並行的一個子集。

並行:是指系統具有同時處理多個任務(動作)的能力

2、同步與異步的區別?

同步: 當進程執行到一個IO(等待外部數據)的時候你---->會一直等:同步 (示例: 打電話)

異步:當進程執行到一個IO(等待外部數據)的時候你---->不等:一直等到數據接收成功,再回來處理。異步效率更高(示例:發短信)

3、任務分為

1、對於IO密集型的任務:  python的多線程是有意義的,可以采用:多進程+協程的方式
2、對於計算密集型的任務:python的多線程就不推薦。python就不適用了。

 

七、同步鎖                                                                                                                                                               

 示例1:不加鎖(拿到的值是不固定的)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 def sub():  9 global num 10 11 # num -= 1 12 temp=num 13 time.sleep(0.001) #別外75個線程,拿到100了,時間不固定。 14 num=temp-1 15 16 num =100 17 18 l=[] 19 20 for i in range(100): 21 t=threading.Thread(target=sub) 22  t.start() 23  l.append(t) 24 25 for t in l: 26  t.join() 27 28 print(num)
復制代碼

執行結果:

1 73 or 75  (這個值是隨機的,會不斷變化)

 

示例2:加鎖 (加鎖的作用:就是把多線程變成串行,結果不會變)

復制代碼
 1 #加鎖的作用:就是把多線程變成串行,結果就不會變)  2  3 #!/usr/bin/env python  4 # -*- coding:utf-8 -*-  5 #Author: nulige  6  7 import threading  8 import time  9 10 def sub(): 11 12 global num 13 14 # num -= 1 15 lock.acquire() #獲取鎖 16 temp=num 17 time.sleep(0.001) 18 num=temp-1 19 lock.release() #釋放鎖 20 21 num =100 22 23 l=[] 24 lock=threading.Lock() 25 26 for i in range(100): 27 t=threading.Thread(target=sub) 28  t.start() 29  l.append(t) 30 31 for t in l: 32  t.join() 33 34 print (num)
復制代碼

執行結果:

1 0

 

GIL:全局解釋器鎖
作用:保證同一時刻,只有一個線程被CPU執行,無論你有多少個線程。

為什么這里還需要lock? 注意啦,這里的lock是用戶級的lock,跟那個GIL沒關系 ,具體我們通過下圖進行講解

  既然用戶程序已經自己有鎖了,那為什么C python還需要GIL呢?加入GIL主要的原因是為了降低程序的開發的復雜度,比如現在的你寫python不需要關心內存回收的問題,因為Python解釋器幫你自動定期進行內存回收,你可以理解為python解釋器里有一個獨立的線程,每過一段時間它起wake up做一次全局輪詢看看哪些內存數據是可以被清空的,此時你自己的程序 里的線程和 py解釋器自己的線程是並發運行的,假設你的線程刪除了一個變量,py解釋器的垃圾回收線程在清空這個變量的過程中的clearing時刻,可能一個其它線程正好又重新給這個還沒來及得清空的內存空間賦值了,結果就有可能新賦值的數據被刪除了,為了解決類似的問題,python解釋器簡單粗暴的加了鎖,即當一個線程運行時,其它人都不能動,這樣就解決了上述的問題,這可以說是Python早期版本的遺留問題。

 

八、線程死鎖和遞歸鎖                                                                                                                                                 

  在線程間共享多個資源的時候,如果兩個線程分別占有一部分資源並且同時等待對方的資源,就會造成死鎖,因為系統判斷這部分資源都

正在使用,所有這兩個線程在無外力作用下將一直等待下去。

示例1:線程死鎖

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 # Author: nulige  4  5 import threading  6 import time  7  8  9 class MyThread(threading.Thread): 10 def actionA(self): 11 A.acquire() # count=1 12 print(self.name, "gotA", time.ctime()) 13 time.sleep(2) 14 15 B.acquire() # count=2 16 print(self.name, "gotB", time.ctime()) 17 time.sleep(1) 18 19 B.release() # count=1 20 A.release() # count=0 21 22 def actionB(self): 23 B.acquire() # count=1 24 print(self.name, "gotB", time.ctime()) 25 time.sleep(2) 26 27 A.acquire() # count=2 28 print(self.name, "gotA", time.ctime()) 29 time.sleep(1) 30 31 A.release() # count=1 32 B.release() # count=0 33 34 def run(self): 35  self.actionA() 36  self.actionB() 37 38 39 if __name__ == '__main__': 40 41 A = threading.Lock() 42 B = threading.Lock() 43 44 L = [] 45 46 for i in range(5): 47 t = MyThread() 48  t.start() 49  L.append(t) 50 51 for i in L: 52  i.join() 53 54 print("ending.....")
復制代碼

執行結果:

1 Thread-1 gotA Mon Jan 16 17:33:58 2017
2 Thread-1 gotB Mon Jan 16 17:34:00 2017 3 Thread-1 gotB Mon Jan 16 17:34:01 2017 4 Thread-2 gotA Mon Jan 16 17:34:01 2017 #死鎖,一直卡在這里

解決辦法:

使用遞歸鎖,將

1
2
lockA = threading.Lock()
lockB = threading.Lock()<br> #--------------<br>lock=threading.RLock()

為了支持在同一線程中多次請求同一資源,python提供了“可重入鎖”:threading.RLock。RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次acquire。直到一個線程所有的acquire都被release,其他的線程才能獲得資源。

 

示例2:遞歸鎖(解決死鎖問題)

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading  6 import time  7  8 class MyThread(threading.Thread):  9 10 def actionA(self): 11 12 r_lcok.acquire() #count=1 13 print(self.name,"gotA",time.ctime()) 14 time.sleep(2) 15 16 r_lcok.acquire() #count=2 17 print(self.name,"gotB",time.ctime()) 18 time.sleep(1) 19 20 r_lcok.release() #count=1 21 r_lcok.release() #count=0 22 23 24 def actionB(self): 25 26 r_lcok.acquire() #count=1 27 print(self.name,"gotB",time.ctime()) 28 time.sleep(2) 29 30 r_lcok.acquire() #count=2 31 print(self.name,"gotA",time.ctime()) 32 time.sleep(1) 33 34 r_lcok.release() #count=1 35 r_lcok.release() #count=0 36 37 def run(self): 38 39  self.actionA() 40  self.actionB() 41 42 if __name__ == '__main__': 43 44 r_lcok=threading.RLock() 45 L=[] 46 47 for i in range(5): 48 t=MyThread() 49  t.start() 50  L.append(t) 51 52 for i in L: 53  i.join() 54 55 print("ending.....")
復制代碼

執行結果:

復制代碼
 1 Thread-1 gotA Mon Jan 16 17:38:42 2017
 2 Thread-1 gotB Mon Jan 16 17:38:44 2017  3 Thread-1 gotB Mon Jan 16 17:38:45 2017  4 Thread-1 gotA Mon Jan 16 17:38:47 2017  5 Thread-3 gotA Mon Jan 16 17:38:48 2017  6 Thread-3 gotB Mon Jan 16 17:38:50 2017  7 Thread-4 gotA Mon Jan 16 17:38:51 2017  8 Thread-4 gotB Mon Jan 16 17:38:53 2017  9 Thread-5 gotA Mon Jan 16 17:38:54 2017 10 Thread-5 gotB Mon Jan 16 17:38:56 2017 11 Thread-5 gotB Mon Jan 16 17:38:57 2017 12 Thread-5 gotA Mon Jan 16 17:38:59 2017 13 Thread-3 gotB Mon Jan 16 17:39:00 2017 14 Thread-3 gotA Mon Jan 16 17:39:02 2017 15 Thread-4 gotB Mon Jan 16 17:39:03 2017 16 Thread-4 gotA Mon Jan 16 17:39:05 2017 17 Thread-2 gotA Mon Jan 16 17:39:06 2017 18 Thread-2 gotB Mon Jan 16 17:39:08 2017 19 Thread-2 gotB Mon Jan 16 17:39:09 2017 20 Thread-2 gotA Mon Jan 16 17:39:11 2017 21 ending.....
復制代碼

 

九、同步條件(Event)                                                                                                                                        

復制代碼
An event is a simple synchronization object;the event represents an internal flag, and threads can wait for the flag to be set, or set or clear the flag themselves. event = threading.Event() # a client thread can wait for the flag to be set event.wait() # a server thread can set or reset it event.set() event.clear() If the flag is set, the wait method doesn’t do anything. If the flag is cleared, wait will block until it becomes set again. Any number of threads may wait for the same event.
復制代碼

示例:

復制代碼
 1 import threading,time  2 class Boss(threading.Thread):  3 def run(self):  4 print("BOSS:今晚大家都要加班到22:00。")  5 print(event.isSet())  6  event.set()  7 time.sleep(5)  8 print("BOSS:<22:00>可以下班了。")  9 print(event.isSet()) 10  event.set() 11 class Worker(threading.Thread): 12 def run(self): 13  event.wait() 14 print("Worker:哎……命苦啊!") 15 time.sleep(1) 16  event.clear() 17  event.wait() 18 print("Worker:OhYeah!") 19 if __name__=="__main__": 20 event=threading.Event() 21 threads=[] 22 for i in range(5): 23  threads.append(Worker()) 24  threads.append(Boss()) 25 for t in threads: 26  t.start() 27 for t in threads: 28 t.join()
復制代碼

執行結果:

復制代碼
 1 BOSS:今晚大家都要加班到22:00 2 False  3 Worker:哎……命苦啊!  4 Worker:哎……命苦啊!  5 Worker:哎……命苦啊!  6 Worker:哎……命苦啊!  7 Worker:哎……命苦啊!  8 BOSS:<22:00>可以下班了。  9 False 10 Worker:OhYeah! 11 Worker:OhYeah! 12 Worker:OhYeah! 13 Worker:OhYeah! 14 Worker:OhYeah! 15 ending.....
復制代碼

 

十、信號量(Semaphore)                                                                                                                                   

指同時開幾個線程並發

    信號量用來控制線程並發數的,BoundedSemaphore或Semaphore管理一個內置的計數 器,每當調用acquire()時-1,調用release()時+1。

  計數器不能小於0,當計數器為 0時,acquire()將阻塞線程至同步鎖定狀態,直到其他線程調用release()。(類似於停車位的概念)

    BoundedSemaphore與Semaphore的唯一區別在於前者將在調用release()時檢查計數 器的值是否超過了計數器的初始值,如果超過了將拋出一個異常。

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import threading,time  6  7 class myThread(threading.Thread):  8 def run(self):  #啟動后,執行run方法  9 if semaphore.acquire(): #加把鎖,可以放進去多個(相當於5把鎖,5個鑰匙,同時有5個線程) 10 print(self.name) 11 time.sleep(5) 12  semaphore.release() 13 14 if __name__=="__main__": 15 semaphore=threading.Semaphore(5) #同時能有幾個線程進去(設置為5就是一次5個線程進去),類似於停車廠一次能停幾輛車 16 17 thrs=[] #空列表 18 for i in range(100): #100個線程 19  thrs.append(myThread()) #加線程對象 20 21 for t in thrs: 22 t.start()  #分別啟動
復制代碼

執行結果:

復制代碼
 1 Thread-1
 2 Thread-2  3 Thread-3  4 Thread-4  5 Thread-5 #5個線程同時出來  6  7 Thread-8  8 Thread-6  9 Thread-9 10 Thread-7 11 Thread-10 #每隔3秒再打印5個出來 12 13 部分省略.......
復制代碼

 

 ( 重點掌握)                                                                                             

列表是不安全的數據結構

示例:

復制代碼
 1 #兩個線程同時刪除5,所以會報錯,因為只有一個5,一個線程刪除了就沒有啦。  2  3 import threading,time  4  5 li=[1,2,3,4,5]  6  7 def pri():  8 while li:  9 a=li[-1] #取值 10 print(a) 11 time.sleep(1) 12 li.remove(a) #remove按索引去刪除內容 13 14 t1=threading.Thread(target=pri,args=()) #線程1 15 t1.start() 16 t2=threading.Thread(target=pri,args=()) #線程2 17 t2.start()
復制代碼

執行結果:

復制代碼
 1 #會報錯,因為只有一個5,刪除了就沒有啦,不能兩個線程同時刪除。  2  3 5  4 5  5 4  6 Exception in thread Thread-2:  7 Traceback (most recent call last):  8 File "C:\Python3.5\lib\threading.py", line 914, in _bootstrap_inner  9  self.run() 10 File "C:\Python3.5\lib\threading.py", line 862, in run 11 self._target(*self._args, **self._kwargs) 12 File "D:/python/day34/s10.py", line 34, in pri 13  li.remove(a) 14 ValueError: list.remove(x): x not in list 15 16 3 17 2 18 1
復制代碼

 

思考:如何通過對列來完成上述功能?

queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.

queue列隊的三種模式及構造函數

1、先進先出模式 (誰先進去,誰先出來)   ---->class queue.Queue(maxsize)

2、先進后出模式  (先進去的,最后出來)  ---->class queue.LifoQueue(maxsize)

3、優先級模式    (優先級越低,先出來)   ---->class queue.PriorityQueue(maxsize)

 

一、先進先出

 示例1:

復制代碼
 1 #先進先出 (原則:誰先進去,誰就先出來)  2 import queue #線程 隊列  3  4 q=queue.Queue() #先進先出  5 q.put(12)  6 q.put("hello")  7 q.put({"name":"yuan"})  8  9 while 1: 10 data=q.get() 11 print(data) 12 print("-----------")
復制代碼

執行結果:

1 12            #他是第1個進去的,所以他先出來 2 ----------- 3 hello 4 ----------- 5 {'name': 'yuan'} 6 -----------

示例2: 

復制代碼
 1 import queue #隊列,解決多線程問題 (注意:python2.7 Queue的首字母是大寫的)  2  3  4 # q=queue.Queue(3) #1、設置3就是滿了,默認(FIFO 先進先出 ) #先進后出(手槍彈夾,后壓進去的,先出來)  5 q=queue.Queue()  6 q.put(12)  7 q.put("hello")  8 q.put({"name":"yuan"})  9 10 q.put(34) 11 # q.put(34,False) #2、blook=True,如果改成Flase,提示你滿了,會報錯,但不會卡在這里 12 13 14 while 1: 15 data=q.get()  #1、會卡着,等值進來 16 # data=q.get(block=False)  #3、隊列為空 17 print(data) 18 print("-----------")
復制代碼

 

二、先進后出

示例:

復制代碼
 1 #先進后出  2 import queue  3  4 q=queue.LifoQueue() #先進后出  5  6 q.put(12)  7 q.put("hello")  8 q.put({"name":"yuan"})  9 10 while 1: 11 data=q.get() #卡着,等值進來, 12 print(data) 13 print("-----------")
復制代碼

執行結果:

1 {'name': 'yuan'}  #后進來的先出去 2 ----------- 3 hello 4 ----------- 5 12 6 -----------

 

三、優化級

示例:

復制代碼
 1 #優先級  2 import queue  3  4 q=queue.PriorityQueue() #優先級  5  6 q.put([3,12])  7 q.put([2,"hello"]) #2先出來,按優化級 級別是:2--->3--->4 從級到高  8 q.put([4,{"name":"yuan"}])  9 10 while 1: 11 data=q.get() 12 print(data[1]) 13 print("-----------------------")
復制代碼

 執行結果:

1 hello                 #2先出來,按優先級 2 ----------------------- 3 12 4 ----------------------- 5 {'name': 'yuan'} 6 -----------------------

 

queue隊列類的方法:

復制代碼
創建一個“隊列”對象
import Queue q = Queue.Queue(maxsize = 10) Queue.Queue類即是一個隊列的同步實現。隊列長度可為無限或者有限。可通過Queue的構造函數的可選參數maxsize來設定隊列長度。如果maxsize小於1就表示隊列長度無限。 將一個值放入隊列中 q.put(10) 調用隊列對象的put()方法在隊尾插入一個項目。put()有兩個參數,第一個item為必需的,為插入項目的值;第二個block為可選參數,默認為 1。如果隊列當前為空且block為1,put()方法就使調用線程暫停,直到空出一個數據單元。如果block為0,put方法將引發Full異常。 將一個值從隊列中取出 q.get() 調用隊列對象的get()方法從隊頭刪除並返回一個項目。可選參數為block,默認為True。如果隊列為空且block為True, get()就使調用線程暫停,直至有項目可用。如果隊列為空且block為False,隊列將引發Empty異常。 此包中的常用方法(q = Queue.Queue()): q.qsize() 返回隊列的大小 q.empty() 如果隊列為空,返回True,反之False q.full() 如果隊列滿了,返回True,反之False q.full 與 maxsize 大小對應 q.get([block[, timeout]]) 獲取隊列,timeout等待時間 q.get_nowait() 相當q.get(False) 非阻塞 q.put(item) 寫入隊列,timeout等待時間 q.put_nowait(item) 相當q.put(item, False) q.task_done() 在完成一項工作之后,q.task_done() 函數向任務已經完成的隊列發送一個信號 q.join() 實際上意味着等到隊列為空,再執行別的操作
復制代碼

 

示例1: q.qsize() and q.empty() and q.full

復制代碼
 1 import queue  2  3 q=queue.Queue()  4 q.put(12)  5 q.put("hello")  6 q.put({"name":"yuan"})  7  8 print(q.qsize()) #判斷隊列大小  9 print(q.empty()) #判斷隊列是否為空 10 print(q.full) #判斷隊列是否滿了 11 12 while 1: 13 data=q.get() 14 print(data) 15 print("-----------")
復制代碼

執行結果:

3 --->q.qsize() False ---->q.empty() <bound method Queue.full of <queue.Queue object at 0x01315A70>> --->full

 

示例2:g.put_nowait() 相當於q.get(Flase)

復制代碼
 1 import queue  2  3 q=queue.Queue(3)  4  5 q.put(12)  6 q.put([2,"hello"])  7 q.put([4,{"name":"yuan"}])  8  9 q.put_nowait(56)  #相當於q.get(Flase) 10 11 while 1: 12 data=q.get() 13 print(data)
復制代碼

執行結果:

復制代碼
1 Traceback (most recent call last): 2 File "D:/python/day34/s7.py", line 79, in <module> 3 q.put_nowait(56) #相當於q.get(Flase) 4 File "C:\Python3.5\lib\queue.py", line 184, in put_nowait 5 return self.put(item, block=False) 6 File "C:\Python3.5\lib\queue.py", line 130, in put 7 raise Full 8 queue.Full
復制代碼

十二、生產者消費者模型                                                                                                                                       

  1、為什么要使用生產者和消費者模式

  在線程世界里,生產者就是生產數據的線程,消費者就是消費數據的線程。在多線程開發當中,如果生產者處理速度很快,而消費者處理速度很慢,那么生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那么消費者就必須等待生產者。為了解決這個問題於是引入了生產者和消費者模式。

  2、什么是生產者消費者模式

  生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列里取,阻塞隊列就相當於一個緩沖區,平衡了生產者和消費者的處理能力。

這就像,在餐廳,廚師做好菜,不需要直接和客戶交流,而是交給前台,而客戶去飯菜也不需要不找廚師,直接去前台領取即可,這也是一個結耦的過程。

示例1:邊做包子,邊吃包子

復制代碼
 1 #生產者消費者模型(生產者先執行,再吃包子。)  2  3 import time,random  4 import queue,threading  5  6 q = queue.Queue()  7  8 def Producer(name):  9 count = 0 10 while count <10: 11 print("making........") 12 time.sleep(random.randrange(3)) #產生一個隨機數(1-2秒之間) 13  q.put(count) 14 print('Producer %s has produced %s baozi..' %(name, count)) 15 count +=1 16 print("ok......") 17 18 def Consumer(name): 19 count = 0 20 while count <10: 21 time.sleep(random.randrange(4)) #產生一個隨機數(1-3秒之間) 22 if not q.empty(): 23 data = q.get() 24 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) 25 else: 26 print("-----no baozi anymore----") 27 count +=1 28 29 p1 = threading.Thread(target=Producer, args=('A君',)) 30 c1 = threading.Thread(target=Consumer, args=('B君',)) 31 32 p1.start() 33 c1.start()
復制代碼

執行結果:

復制代碼
 1 making........  2 Producer A君 has produced 0 baozi..  3 ok......  4 making........  5 Consumer B君 has eat 0 baozi...  6 Producer A君 has produced 1 baozi..  7 ok......  8 making........  9 Consumer B君 has eat 1 baozi... 10 Producer A君 has produced 2 baozi.. 11 ok...... 12 making........ 13 Consumer B君 has eat 2 baozi... 14 Producer A君 has produced 3 baozi.. 15 ok...... 16 making........ 17 Producer A君 has produced 4 baozi.. 18 ok...... 19 making........ 20 Consumer B君 has eat 3 baozi... 21 Producer A君 has produced 5 baozi.. 22 ok...... 23 making........ 24 Producer A君 has produced 6 baozi.. 25 ok...... 26 making........ 27 Consumer B君 has eat 4 baozi... 28 Producer A君 has produced 7 baozi.. 29 ok...... 30 making........ 31 Producer A君 has produced 8 baozi.. 32 ok...... 33 making........ 34 Producer A君 has produced 9 baozi.. 35 ok...... 36 Consumer B君 has eat 5 baozi... 37 Consumer B君 has eat 6 baozi... 38 Consumer B君 has eat 7 baozi... 39 Consumer B君 has eat 8 baozi... 40 Consumer B君 has eat 9 baozi... 41 -----no baozi anymore---- 42 -----no baozi anymore---- 43 -----no baozi anymore---- 44 -----no baozi anymore---- 45 -----no baozi anymore---- 46 -----no baozi anymore---- 47 -----no baozi anymore---- 48 -----no baozi anymore---- 49 -----no baozi anymore---- 50 -----no baozi anymore----
復制代碼

 

示例2: 供不應求,吃包子的人太多了(1個人在生產包子,3個人在吃包子)

復制代碼
 1 #生產者消費者模型(供不應求,吃的人太多了,生產不贏)  2  3 import time,random  4 import queue,threading  5  6 q = queue.Queue()  7  8 def Producer(name):  9 count = 0 10 while count <10: 11 print("making........") 12 time.sleep(random.randrange(3)) #產生一個隨機數(1-2秒之間) 13  q.put(count) 14 print('Producer %s has produced %s baozi..' %(name, count)) 15 count +=1 16 print("ok......") 17 18 def Consumer(name): 19 count = 0 20 while count <10: 21 time.sleep(random.randrange(4)) #產生一個隨機數(1-3秒之間) 22 if not q.empty(): 23 data = q.get() 24 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) 25 else: 26 print("-----no baozi anymore----") 27 count +=1 28 29 p1 = threading.Thread(target=Producer, args=('A君',)) #1個人生產包子 30 c1 = threading.Thread(target=Consumer, args=('B君',)) 31 c2 = threading.Thread(target=Consumer, args=('C君',)) #3個人在吃包子,導致吃包子的人太多啦,生產不贏 32 c3 = threading.Thread(target=Consumer, args=('D君',)) 33 34 p1.start() 35 c1.start() 36 c2.start() 37 c3.start()
復制代碼

執行結果:

復制代碼
 1 making........  2 -----no baozi anymore---- #生產不贏,供不應求,吃包子的人太多了  3 -----no baozi anymore----  4 Producer A君 has produced 0 baozi..  5 ok......  6 making........  7 Producer A君 has produced 1 baozi..  8 ok......  9 making........ 10 Consumer C君 has eat 0 baozi... 11 Consumer D君 has eat 1 baozi... 12 -----no baozi anymore---- 13 -----no baozi anymore---- 14 -----no baozi anymore---- 15 -----no baozi anymore---- 16 -----no baozi anymore---- 17 -----no baozi anymore---- 18 Producer A君 has produced 2 baozi.. 19 ok...... 20 making........ 21 Producer A君 has produced 3 baozi.. 22 ok...... 23 making........ 24 Producer A君 has produced 4 baozi.. 25 ok...... 26 making........ 27 Consumer C君 has eat 2 baozi... 28 Consumer D君 has eat 3 baozi... 29 Consumer D君 has eat 4 baozi... 30 -----no baozi anymore---- 31 -----no baozi anymore---- 32 Producer A君 has produced 5 baozi.. 33 ok...... 34 making........ 35 Producer A君 has produced 6 baozi.. 36 ok...... 37 making........ 38 Producer A君 has produced 7 baozi.. 39 ok...... 40 making........ 41 Producer A君 has produced 8 baozi.. 42 ok...... 43 making........ 44 Consumer B君 has eat 5 baozi... 45 Consumer C君 has eat 6 baozi... 46 Consumer D君 has eat 7 baozi... 47 Producer A君 has produced 9 baozi.. 48 ok...... 49 Consumer B君 has eat 8 baozi... 50 Consumer C君 has eat 9 baozi... 51 -----no baozi anymore---- 52 -----no baozi anymore---- 53 -----no baozi anymore---- 54 -----no baozi anymore---- 55 -----no baozi anymore---- 56 -----no baozi anymore---- 57 -----no baozi anymore---- 58 -----no baozi anymore---- 59 -----no baozi anymore---- 60 -----no baozi anymore---- 61 -----no baozi anymore---- 62 -----no baozi anymore---- 63 -----no baozi anymore---- 64 -----no baozi anymore---- 65 -----no baozi anymore---- 66 -----no baozi anymore---- 67 -----no baozi anymore---- 68 -----no baozi anymore---- 69 -----no baozi anymore---- 70 -----no baozi anymore----
復制代碼

 

示例3:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import time,random  6 import queue,threading  7  8 q = queue.Queue()  9 10 def Producer(name): 11 count = 0 12 while count <10: 13 print("making........") 14 time.sleep(5) 15  q.put(count) 16 print('Producer %s has produced %s baozi..' %(name, count)) 17 count +=1 18 q.task_done() #發信號告訴隊列在生產包子,讓join接收,就開始吃包子 19 print("ok......") 20 21 def Consumer(name): 22 count = 0 23 while count <10: 24 time.sleep(random.randrange(4)) #產生一個隨機數(1秒-3秒之間) 25 print("waiting...等待包子做的過程中...") 26 q.join() #join開始接收 27 data = q.get() 28 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) 29 count +=1 30 31 p1 = threading.Thread(target=Producer, args=('A君',)) 32 c1 = threading.Thread(target=Consumer, args=('B君',)) 33 c2 = threading.Thread(target=Consumer, args=('C君',)) 34 c3 = threading.Thread(target=Consumer, args=('D君',)) 35 36 p1.start() 37 c1.start() 38 c2.start() 39 c3.start()
復制代碼

執行結果:

復制代碼
 1 making........  2 waiting...等待包子做的過程中...  3 waiting...等待包子做的過程中...  4 waiting...等待包子做的過程中...  5 Producer A君 has produced 0 baozi..  6 ok......  7 making........  8 Consumer D君 has eat 0 baozi...  9 waiting...等待包子做的過程中... 10 Producer A君 has produced 1 baozi.. 11 Consumer B君 has eat 1 baozi... 12 waiting...等待包子做的過程中... 13 ok...... 14 making........ 15 部分代碼省略......
復制代碼

 

示例4:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 import time,random  6 import queue,threading  7  8 q = queue.Queue()  9 10 def Producer(name): 11 count = 0 12 while count <10: 13 print("making.....正在制作包子...") 14 time.sleep(5) 15  q.put(count) 16 print('Producer %s has produced %s baozi..' %(name, count)) 17 count +=1 18  q.join() 19 print("ok......") 20 21 def Consumer(name): 22 count = 0 23 while count <10: 24 time.sleep(random.randrange(4)) #產生一個隨機數(1秒-3秒之間) 25 data = q.get() 26 print("eating.......") 27 time.sleep(4) #4秒鍾這后 28 q.task_done() #給他發一個信號,才打印ok 29 print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data)) 30 count +=1 31 32 p1 = threading.Thread(target=Producer, args=('A君',)) 33 c1 = threading.Thread(target=Consumer, args=('B君',)) 34 c2 = threading.Thread(target=Consumer, args=('C君',)) 35 c3 = threading.Thread(target=Consumer, args=('D君',)) 36 37 p1.start() 38 c1.start() 39 c2.start() 40 c3.start()
復制代碼

執行結果:

復制代碼
 1 making.....正在制作包子...  2 Producer A君 has produced 0 baozi..  3 eating.......  4 Consumer B君 has eat 0 baozi...  5 ok......  6 making.....正在制作包子...  7 Producer A君 has produced 1 baozi..  8 eating.......  9 Consumer C君 has eat 1 baozi... 10 ok...... 11 making.....正在制作包子... 12 Producer A君 has produced 2 baozi.. 13 eating....... 14 Consumer D君 has eat 2 baozi... 15 ok...... 16 making.....正在制作包子... 17 Producer A君 has produced 3 baozi.. 18 eating....... 19 Consumer B君 has eat 3 baozi... 20 ok...... 21 making.....正在制作包子... 22 Producer A君 has produced 4 baozi.. 23 eating....... 24 Consumer C君 has eat 4 baozi... 25 ok...... 26 making.....正在制作包子... 27 Producer A君 has produced 5 baozi.. 28 eating....... 29 Consumer D君 has eat 5 baozi... 30 ok...... 31 making.....正在制作包子... 32 Producer A君 has produced 6 baozi.. 33 eating....... 34 Consumer B君 has eat 6 baozi... 35 ok...... 36 making.....正在制作包子...
復制代碼

總結:

  task_done和join必須成對出現,類似於一個通信工具,我給你發個信號,你就知道我做了某個操作

(例如:put or get) 對方就是join。如果我put or get 你就處理。(類似於收到信號就處理)

類似於,我發信號,你收到就處理,沒收到就Join卡住,一直在那等待。

十三、多進程模塊 multiprocessing (主要解決GIL問題)                                

Multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency,effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

由於GIL的存在,python中的多線程其實並不是真正的多線程,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進程。

multiprocessing包是Python中的多進程管理包。與threading.Thread類似,它可以利用multiprocessing.Process對象來創建一個進程。該進程可以運行在Python程序內部編寫的函數。該Process對象與Thread對象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition類 (這些對象可以像多線程那樣,通過參數傳遞給各個進程),用以同步進程,其用法與threading包中的同名類一致。所以,multiprocessing的很大一部份與threading使用同一套API,只不過換到了多進程的情景。

 

一、進程的調用

調用方式1:

復制代碼
 1 #!/usr/bin/env python  2 # -*- coding:utf-8 -*-  3 #Author: nulige  4  5 #多進程調用(並行)  6  7 from multiprocessing import Process  8 import time  9 10 11 def f(name): 12 time.sleep(1) 13 print('hello', name,time.ctime()) 14 15 if __name__ == '__main__': 16 p_list=[] 17 for i in range(3): #子進程 18 19 p = Process(target=f, args=('alvin',)) 20  p_list.append(p) 21  p.start() 22 23 for i in p_list: 24  i.join() 25 26 print('end') #主進程
復制代碼

執行結果:

1 hello alvin Mon Jan 16 18:38:08 2017  #並行,三個同時出現 2 hello alvin Mon Jan 16 18:38:08 2017 3 hello alvin Mon Jan 16 18:38:08 2017 4 end

 

調用方式2:

示例1: 1秒鍾這后,4條消息同時執行

復制代碼
 1 from multiprocessing import Process  2 import time  3  4 class MyProcess(Process):  5  6 def run(self):  7 time.sleep(1)  8 print ('hello', self.name,time.ctime())  9 10 11 if __name__ == '__main__': 12 p_list=[] 13 for i in range(3): 14 p = MyProcess() #進程對象 15  p.start() #啟動執行run方法 16  p_list.append(p) 17 18 for p in p_list: 19 p.join() #子進程沒有執行完,主進程會一直等待 20 21 print('end')
復制代碼

執行結果:

1 hello MyProcess-1 Mon Jan 16 18:56:58 2017  #結果同時出來 2 hello MyProcess-2 Mon Jan 16 18:56:58 2017 3 hello MyProcess-3 Mon Jan 16 18:56:58 2017 4 end

 

示例2:daemon=True 是屬性,不是方法

復制代碼
 1 #設置為守護進程,打印的就是end  2  3 from multiprocessing import Process  4 import time  5  6 class MyProcess(Process):  7  8 def run(self):  9 time.sleep(1) 10 print ('hello', self.name,time.ctime()) 11 12 if __name__ == '__main__': 13 p_list=[] 14 15 for i in range(3): 16 p = MyProcess() #進程對象 17 p.daemon=True #是屬性,不是方法 18 p.start() #啟動執行sun方法 19  p_list.append(p) 20 21 print('end') #主進程執行完之后,不管守護進程
復制代碼

執行結果:

1 end

 

調用方法3:

復制代碼
 1 from multiprocessing import Process  2 import os  3 import time  4  5 def info(title):  6 print("title:", title)  7 print('parent process:', os.getppid()) #父進程的pid  8 print('process id:', os.getpid()) #打印進程號 9 10 def f(name): 11 info('function f') 12 print('hello', name) 13 14 if __name__ == '__main__': 15 info('main process line') 16 17 time.sleep(1) 18 print("------------------") 19 p = Process(target=info, args=('yuan',)) 20  p.start() 21 p.join()
復制代碼

執行結果:

復制代碼
1 title: main process line 2 parent process: 4204 3 process id: 7280 4 ------------------ 5 title: yuan 6 parent process: 7280 7 process id: 3596
復制代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM