Python多線程、阻塞線程、線程同步和守護線程實例詳解

  • 2020 年 2 月 10 日
  • 筆記

一、多線程(主線程和子線程同時執行)

1、主線程是程序本身,看不到的,主線程和子線程沒有依賴關係,同步執行的,若主線程先執行完,會等子線程執行完畢,程序結束

2、啟動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行run()

3、threading.currentThread(): 返回當前的線程變量<Thread(Thread-1, started 8056)>、<_MainThread(MainThread, started 14400)>

threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程;

threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果

run(): 用以表示線程活動的方法

start():啟動線程活動

join(timeout): 等待至線程中止。這阻塞調用線程直至線程的join() 方法被調用中止-正常退出或者拋出未處理的異常-或者是可選的超時發生;

sAlive(): 返回線程是否活動的

getName(): 返回線程名;setName(): 設置線程名

4、多線程實例

#函數式多線程  import time,threading  def learnEnglish():      print('%s 橙子在學習英語 %s'%(threading.currentThread(),time.ctime()))  def learnPython(name):      print('%s %s在學習英語 %s'%(threading.currentThread(),name,time.ctime()))  def learnC(name,course):      print('%s %s在學習%s %s'%(threading.currentThread(),name,course,time.ctime()))  start_time=time.time()  threads=[]# 創建線程數組  thread1=threading.Thread(target=learnEnglish)#創建線程  thread2=threading.Thread(target=learnPython,args=('橙汁',))  thread3=threading.Thread(target=learnC,args=('檸檬','C',))  thread4=threading.Thread(target=learnC,kwargs={"name":"王荔","course":"測試"})  threads.append(thread1)#將線程1添加到線程數組  threads.append(thread2)  threads.append(thread3)  threads.append(thread4)  for i in threads:      i.start()#啟動線程  run_times=(time.time()-start_time)  print('%s %s'%(threading.currentThread(),time.ctime()))  print('主線程和子線程運行時間共:%s'%run_times)      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  <Thread(Thread-1, started 5664)> 橙子在學習英語 Thu Mar 14 13:12:25 2019  <Thread(Thread-2, started 6964)> 橙汁在學習英語 Thu Mar 14 13:12:25 2019  <Thread(Thread-3, started 17908)> 檸檬在學習C Thu Mar 14 13:12:25 2019  <Thread(Thread-4, started 17816)> 王荔在學習測試 Thu Mar 14 13:12:25 2019  <_MainThread(MainThread, started 12276)> Thu Mar 14 13:12:25 2019  主線程和子線程運行時間共:0.0009965896606445312    Process finished with exit code 0
#多線程threading之封裝  import threading,time  class MyThread(threading.Thread):#繼承父類threading.Thread      def __init__(self,name,people):          '''重寫threading.Thread初始化內容'''          super(MyThread,self).__init__()          self.threadName=name          self.people=people      def run(self):# 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          print('開始線程%s %s'%(self.threadName,time.ctime()))          print('結束線程%s %s'%(self.threadName,time.ctime()))  start_time=time.time()  print('開始主線程%s'%time.ctime())  thread1=MyThread('Thread-1','王荔')  thread2=MyThread('Thread-2','橙子')  thread1.start()  thread2.start()  print('退出主線程%s'%time.ctime())  run_times=(time.time()-start_time)  print('運行時間%s'%run_times)      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 13:16:10 2019  開始線程Thread-1 Thu Mar 14 13:16:10 2019  結束線程Thread-1 Thu Mar 14 13:16:10 2019  開始線程Thread-2 Thu Mar 14 13:16:10 2019  退出主線程Thu Mar 14 13:16:10 2019  運行時間0.0009996891021728516  結束線程Thread-2 Thu Mar 14 13:16:10 2019    Process finished with exit code 0    -----可以看到主線程和子線程是同時運行的,主線程運行完,子線程可能還在運行;子線程運行完,主線程可能還在運行

二、多線程之線程阻塞,子線程.join()(設置在start之後,等所有阻塞線程運行完,再運行主線程)

1、阻塞主線程必須在start()方法後執行,t1.join()等線程1運行完,t2.join()等線程2運行完,再運行主線程

2、如果想讓主線程等待子線程結束後再運行,就需用到【子線程.join()】,此方法是在start後(與setDaemon相反)

3、join(timeout)此方法有個timeout參數,是線程超時時間設置

4、阻塞線程和非阻塞線程實例

#非阻塞線程,主線程休眠1s,子線程休眠3s  時間未統計到子線程,只統計到主線程的,說明主線程和子線程是同步執行的,且主線程執行完了,子線程還在執行    import threading,time  class MyThread(threading.Thread):#繼承父類threading.Thread      def __init__(self,name,people):          '''重寫threading.Thread初始化內容'''          super(MyThread,self).__init__()          self.threadName=name          self.people=people      def run(self):# 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          print('開始線程%s %s'%(self.threadName,time.ctime()))          time.sleep(3)          print('結束線程%s %s'%(self.threadName,time.ctime()))  start_time=time.time()  print('開始主線程%s'%time.ctime())  thread1=MyThread('Thread-1','王荔')  thread2=MyThread('Thread-2','橙子')  thread1.start()  thread2.start()  #thread1.join()  #thread2.join()  print('退出主線程%s'%time.ctime())  time.sleep(1)  run_times=(time.time()-start_time)      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 13:30:07 2019  開始線程Thread-1 Thu Mar 14 13:30:07 2019  開始線程Thread-2 Thu Mar 14 13:30:07 2019  退出主線程Thu Mar 14 13:30:07 2019  運行時間1.0023198127746582  結束線程Thread-1 Thu Mar 14 13:30:10 2019  結束線程Thread-2 Thu Mar 14 13:30:10 2019    Process finished with exit code 0
#阻塞線程1、阻塞線程2,主線程休眠1s,線程1和線程2休眠3s  主線程會等線程1和線程2執行完,才會繼續執行主線程,統計時間為主線程1s+子線程3s=4s  import threading,time  class MyThread(threading.Thread):#繼承父類threading.Thread      def __init__(self,name,people):          '''重寫threading.Thread初始化內容'''          super(MyThread,self).__init__()          self.threadName=name          self.people=people      def run(self):# 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          print('開始線程%s %s'%(self.threadName,time.ctime()))          time.sleep(3)          print('結束線程%s %s'%(self.threadName,time.ctime()))  start_time=time.time()  print('開始主線程%s'%time.ctime())  thread1=MyThread('Thread-1','王荔')  thread2=MyThread('Thread-2','橙子')  thread1.start()  thread2.start()  thread1.join()#阻塞線程1  thread2.join()#阻塞線程2  time.sleep(1)  print('退出主線程%s'%time.ctime())  run_times=(time.time()-start_time)  print('運行時間%s'%run_times)    C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 13:35:29 2019  開始線程Thread-1 Thu Mar 14 13:35:29 2019  開始線程Thread-2 Thu Mar 14 13:35:29 2019  結束線程Thread-1 Thu Mar 14 13:35:32 2019  結束線程Thread-2 Thu Mar 14 13:35:32 2019  退出主線程Thu Mar 14 13:35:33 2019  運行時間4.004500389099121    Process finished with exit code 0
#阻塞線程實例    # coding=utf-8  import threading  import time    def chiHuoGuo(people):      print("%s 吃火鍋的小夥伴-羊肉:%s" % (time.ctime(),people))      time.sleep(1)      print("%s 吃火鍋的小夥伴-魚丸:%s" % (time.ctime(),people))      class myThread (threading.Thread):   # 繼承父類threading.Thread      def __init__(self, people, name):          '''重寫threading.Thread初始化內容'''          threading.Thread.__init__(self)          self.threadName = name          self.people = people        def run(self):   # 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          '''重寫run方法'''          print("開始線程: " + self.threadName)            chiHuoGuo(self.people)     # 執行任務          print("qq交流群:226296743")          print("結束線程: " + self.name)    print("yoyo請小夥伴開始吃火鍋:!!!")    # 創建新線程  thread1 = myThread("xiaoming", "Thread-1")  thread2 = myThread("xiaowang", "Thread-2")    # 開啟線程  thread1.start()  thread2.start()    # 阻塞主線程,等子線程結束  thread1.join()  thread2.join()    time.sleep(0.1)  print("退出主線程:吃火鍋結束,結賬走人")      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  yoyo請小夥伴開始吃火鍋:!!!  開始線程: Thread-1  Thu Mar 14 13:41:10 2019 吃火鍋的小夥伴-羊肉:xiaoming  開始線程: Thread-2  Thu Mar 14 13:41:10 2019 吃火鍋的小夥伴-羊肉:xiaowang  Thu Mar 14 13:41:11 2019 吃火鍋的小夥伴-魚丸:xiaowang  qq交流群:226296743  結束線程: Thread-2  Thu Mar 14 13:41:11 2019 吃火鍋的小夥伴-魚丸:xiaoming  qq交流群:226296743  結束線程: Thread-1  退出主線程:吃火鍋結束,結賬走人    Process finished with exit code 0

三、守護線程(設置在start之前,設置子線程A為守護線程,主線程所在的進程內所有非守護線程統統運行完畢 ,無論子線程A有沒有結束,程序都結束

1、主線程退出時,不等那些子線程完成,那就設置子線程為守護線程thread1.setDaemon(True)

2、設置一個線程為守護線程,就表示你在說這個線程不重要,在進程退出時,不用等待這個線程退出

3、程序在等待子線程結束,才退出,不需要設置線程守護,或者顯示調用thread1.setDaemon(False)

4、主線程是非守護線程,只要還存在一個非守護線程,程序就不會退出。

5、守護線程必須在start()方法調用之前設置,如果不設置為守護線程,程序會被無限掛起

6、當有多個子線程時,守護線程就會等待所有的子線程運行完畢後,守護線程才會掛掉(這一點和主線程是一樣的,都是等待所有的子線程運行完畢後才會掛掉)。

7、調用線程對象的方法setDaemon(true),則可以將其設置為守護線程。在python中建議使用的是thread.demon = true 使用這個方法可以檢測數據合法性

8、setDaemon(True)此方法裏面參數設置為True才會生效

9、對於主線程運行完畢,指的是主線程所在的進程內所有非守護線程統統都運行完畢,主線程才算運行完畢

10、守護線程實例

#設置線程1和線程2為守護線程  因為程序沒有其他非守護線程,所以當主線程運行完,不等線程1和線程2,就直接結束    import threading,time  class MyThread(threading.Thread):#繼承父類threading.Thread      def __init__(self,name,people):          '''重寫threading.Thread初始化內容'''          super(MyThread,self).__init__()          self.threadName=name          self.people=people      def run(self):# 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          time.sleep(3)          print('開始線程%s %s'%(self.threadName,time.ctime()))          print('結束線程%s %s'%(self.threadName,time.ctime()))  start_time=time.time()  print('開始主線程%s'%time.ctime())  thread1=MyThread('Thread-1','王荔')  thread2=MyThread('Thread-2','橙子')  thread1.setDaemon(True)#設置為守護線程  thread2.setDaemon(True)#設置為守護線程  thread1.start()  thread2.start()  print('退出主線程%s'%time.ctime())  run_times=(time.time()-start_time)  print('運行時間%s'%run_times)    C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 13:59:09 2019  退出主線程Thu Mar 14 13:59:09 2019  運行時間0.0009975433349609375    Process finished with exit code 0
#將線程2設置為守護線程  當線程1和主線程運行完,程序立即結束;故會存在2種結果:  當線程1先結束,就不會執行線程2結束  當線程2先結束,就會出執行線程2結束    import threading,time  class MyThread(threading.Thread):#繼承父類threading.Thread      def __init__(self,name,people):          '''重寫threading.Thread初始化內容'''          super(MyThread,self).__init__()          self.threadName=name          self.people=people      def run(self):# 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          print('開始線程%s %s'%(self.threadName,time.ctime()))          time.sleep(3)          print('結束線程%s %s'%(self.threadName,time.ctime()))  start_time=time.time()  print('開始主線程%s'%time.ctime())  thread1=MyThread('Thread-1','王荔')  thread2=MyThread('Thread-2','橙子')  #thread1.setDaemon(True)#設置為守護線程  thread2.setDaemon(True)#設置為守護線程  thread2.start()  thread1.start()  print('退出主線程%s'%time.ctime())  run_times=(time.time()-start_time)  print('運行時間%s'%run_times)      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 14:11:07 2019  開始線程Thread-2 Thu Mar 14 14:11:07 2019  開始線程Thread-1 Thu Mar 14 14:11:07 2019  退出主線程Thu Mar 14 14:11:07 2019  運行時間0.0009958744049072266  結束線程Thread-2 Thu Mar 14 14:11:10 2019  結束線程Thread-1 Thu Mar 14 14:11:10 2019    Process finished with exit code 0      C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  開始主線程Thu Mar 14 14:13:03 2019  開始線程Thread-2 Thu Mar 14 14:13:03 2019  開始線程Thread-1 Thu Mar 14 14:13:03 2019  退出主線程Thu Mar 14 14:13:03 2019  運行時間0.0009908676147460938  結束線程Thread-1 Thu Mar 14 14:13:06 2019    Process finished with exit code 0
#守護線程實例    # coding=utf-8  import threading  import time    def chiHuoGuo(people):      print("%s 吃火鍋的小夥伴-羊肉:%s" % (time.ctime(),people))      time.sleep(1)      print("%s 吃火鍋的小夥伴-魚丸:%s" % (time.ctime(),people))      class myThread (threading.Thread):   # 繼承父類threading.Thread      def __init__(self, people, name):          '''重寫threading.Thread初始化內容'''          threading.Thread.__init__(self)          self.threadName = name          self.people = people        def run(self):   # 把要執行的代碼寫到run函數裏面 線程在創建後會直接運行run函數          '''重寫run方法'''          print("開始線程: " + self.threadName)            chiHuoGuo(self.people)     # 執行任務          print("qq交流群:226296743")          print("結束線程: " + self.name)    print("yoyo請小夥伴開始吃火鍋:!!!")    # 創建新線程  thread1 = myThread("xiaoming", "Thread-1")  thread2 = myThread("xiaowang", "Thread-2")    # 守護線程setDaemon(True)  thread1.setDaemon(True)       # 必須在start之前  thread2.setDaemon(True)    # 開啟線程  thread1.start()  thread2.start()    time.sleep(0.1)  print("退出主線程:吃火鍋結束,結賬走人")    C:UserswangliPycharmProjectsAutoMationvenvScriptspython.exe C:/Users/wangli/PycharmProjects/AutoMation/case/test.py  yoyo請小夥伴開始吃火鍋:!!!  開始線程: Thread-1  Thu Mar 14 14:22:20 2019 吃火鍋的小夥伴-羊肉:xiaoming  開始線程: Thread-2  Thu Mar 14 14:22:20 2019 吃火鍋的小夥伴-羊肉:xiaowang  退出主線程:吃火鍋結束,結賬走人    Process finished with exit code 0