一文看懂python多執行緒

TestingGDR發表於2018-10-27

程式、程式及執行緒的區別

計算機程式是磁碟中可執行的二進位制資料(或者其他型別)他們只有在被讀取到記憶體中,被作業系統呼叫才開始他們的生命週期。

程式是程式的一次執行,每個程式都有自己的地址空間,記憶體,資料棧,以及其他記錄其執行軌跡的輔助資料,作業系統管理再其上面執行的所有程式,併為這些程式公平得分配時間。

執行緒與程式相似,不同的是所有的執行緒都執行在同一個程式中,共享相同的執行環境。

 

1.單執行緒

單執行緒時,當處理器需要處理多個任務時,必須對這些任務安排執行的順序,並按照這個順序來執行任務。

 from time import sleep, ctime
  
  
  # 聽音樂
  def music():
      print('i was listening to music! %s' % ctime())
      sleep(2)
  
  
 # 看電影
 def movie():
     print('i was at the movies! %s' % ctime())
     sleep(5)
 
 
 if __name__ == '__main__':
     music()
     movie()
     print('all end:', ctime())

增加迴圈功能:

 from time import sleep, ctime
  
  
  # 聽音樂
  def music(func, loop):
      for i in range(loop):
          print('i was listening to music! %s !%s' % (func, ctime()))
            sleep(2)
  
 
 # 看電影
 def movie(func, loop):
     for i in range(loop):
         print('i was listening to music! %s !%s' % (func, ctime()))
         sleep(5)
 
 
 if __name__ == '__main__':
     music('愛情買賣', 2)
     movie('一代宗師', 2)
     print('all end:', ctime())

 

給music()和movie()兩個函式設定引數:播放檔案和播放次數。函式中通過for迴圈控制播放的次數。

2、多執行緒

python通過兩個標準庫thread和threading提供對執行緒的支援。thread提供了低階別的,原始的執行緒以及一個簡單的鎖。threading基於Java的執行緒模型設計。鎖(lock)和條件變數(condition)在Java中時物件的基本行為(每個物件都自帶了鎖和條件變數),而在python中則是獨立的物件。

(1)threading模組

避免使用thread模組,原因是它不支援守護執行緒。當主執行緒退出時,所有的子執行緒不關他們是否還在工作,都會被強行退出。但是我們並不希望發生這種行為。就要引入守護執行緒的概念。threading支援守護執行緒。

 from time import sleep, ctime
 import threading
  
  
  # 聽音樂
  def music(func, loop):
      for i in range(loop):
          print('i was listening to music! %s !%s' % (func, ctime()))
          sleep(2)
 
 
 # 看電影
 def movie(func, loop):
     for i in range(loop):
         print('i was listening to music! %s !%s' % (func, ctime()))
         sleep(5)
 
 
 # 建立執行緒陣列
 threads = []
 # 建立執行緒t1,並新增到執行緒陣列
 t1 = threading.Thread(target=music, args=('愛情買賣', 2))
 threads.append(t1)
 # 建立執行緒t2,並新增到執行緒陣列
 t2 = threading.Thread(target=music, args=('一代宗師', 2))
 threads.append(t2)
 
 if __name__ == '__main__':
     # 啟動執行緒
     for t in threads:
         t.start()
     # 守護執行緒
     for t in threads:
         t.join()
 
     print('all end:', ctime())

注:import threading: 引入執行緒模組

       threads = []:建立執行緒陣列,用於裝載執行緒。

       threading.Thread(): 通過呼叫threading模組的Thread()方法來建立執行緒。

執行結果如下:

從上面執行的結果可以看出,兩個子執行緒(music,movie)同時啟動於10分15秒,知道所有執行緒結束於10分17秒共好使2秒。從執行的結果可以看出兩個執行緒達到了並行工作。

優化執行緒的建立

從上面例子中發現執行緒的建立很麻煩,每建立一個執行緒都需要一個t(t1,t2.。。。。)當建立的執行緒較多時,這樣的操作及其的不方便。

 

 from time import sleep, ctime
 import threading
  
  
  # 建立超級播放器
  def super_player(file_, loop):
      for i in range(2):
          print('start playing: %s !%s' % (file_, ctime()))
          sleep(2)
 # 播放檔案與播放時長
 lists = {'愛情買賣.mp3':3,'阿凡達.mp4':5,'傳奇.mp3':4}
 
 threads = []
 files = range(len(lists))
 print(files)
 # 建立執行緒
 print(lists.items())
 for file_,time in lists.items():
     t = threading.Thread(target=super_player,args=(file_,time))
     print(t)
     threads.append(t)
 
 if __name__ == '__main__':
     # 啟動執行緒
     for t in files:
         threads[t].start()
     # 守護執行緒
     for t in files:
         threads[t].join()
 
     print(' end:%s'% ctime())

 

 

 

  from time import sleep, ctime
  import threading
  
  
  # 建立超級播放器
  def super_player(file_, loop):
      for i in range(3):  # 控制執行緒的迴圈次數
          print('start playing: %s !%s' % (file_, ctime()))
          sleep(3)  # 每次迴圈的間接次數
 
 
 # 播放檔案與播放時長
 lists = {'愛情買賣.mp3': 3, '阿凡達.mp4': 5, '傳奇.mp3': 4}
 
 threads = []
 files = range(len(lists))
 print(len(lists))
 
 print(files)  # 列印的結果是range(0,3)
 # 建立執行緒
 print(lists.items())
 for file_, time in lists.items():
     t = threading.Thread(target=super_player, args=(file_, time))
     print(t)
     threads.append(t)
 
 if __name__ == '__main__':
     # 啟動執行緒
     for t in files:
         threads[t].start()
     # 守護執行緒
     for t in files:
         threads[t].join()
 
     print(' end:%s' % ctime())

 

 建立了一個super_player()函式,這個函式可以接收播放檔案和播放時長,可以播放任何檔案。

建立了一個lists字典用於存放播放檔名與時長,通過for迴圈讀取字典,並呼叫super_play()函式建立字典,接著將建立的字典都追加到threads陣列中。

最後通過迴圈啟動執行緒陣列threads中的執行緒。

 

建立執行緒類

 

  import threading
  from time import sleep, ctime
  
 
  # 建立執行緒類
   class MyThread (threading.Thread):
      def __init__(self, func, args, name=''):
          threading.Thread.__init__ (self)
          self.func = func
         self.args = args
         self.name = name
 
     def run(self):
         self.func (*self.args)
 
 
 # 建立超級播放器
 def super_player(file_, loop):
     for i in range (3):  # 控制執行緒的迴圈次數
         print ('start playing: %s !%s' % (file_, ctime ()))
         sleep (3)  # 每次迴圈的間接次數
 
 
 # 播放檔案與播放時長
 lists = {'愛情買賣.mp3': 3, '阿凡達.mp4': 5, '傳奇.mp3': 4}
 
 threads = []
 files = range (len (lists))
 print (len (lists))
 
 print (files)  # 列印的結果是range(0,3)
 # 建立執行緒
 print (lists.items ())
 for file_, time in lists.items ():
     t = threading.Thread (target=super_player, args=(file_, time))
     print (t)
     threads.append (t)
 
 if __name__ == '__main__':
     # 啟動執行緒
     for t in files:
         threads[t].start ()
     # 守護執行緒
     for t in files:
         threads[t].join ()
 
     print (' end:%s' % ctime ())

 

MyThread(threading.Thread)

建立MyThread類,用於繼承threading.Thread類

__init__()類的初始化方法對func,args,name等引數進行初始化。

self.func (*self.args)函式的作用是當函式引數已經存在於一個元組或者字典中時,apply()間接地呼叫函式。args是一個包含將要提供給函式的按位置傳遞的引數元組。
如果省略了args,則任何引數不會被傳遞,kwargs是一個包含關鍵字引數的字典

結語

對測試技術感興趣的同學,歡迎加QQ群175317069,一起學習,相互討論。

群內已經有小夥伴將知識體系整理好(原始碼,筆記,PPT,學習視訊),歡迎加群免費領取

加QQ群175317069,免費領取資料

相關文章