1.線程,線程創建 概念:在傳統操作系統中,每個進程有一個地址空間,而且預設就有一個控制線程,線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個進程,車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線。流水線的工作需要電源,電源就相當於c ...
1.線程,線程創建
概念:在傳統操作系統中,每個進程有一個地址空間,而且預設就有一個控制線程,線程顧名思義,就是一條流水線工作的過程,一條流水線必須屬於一個車間,一個車間的工作過程是一個進程,車間負責把資源整合到一起,是一個資源單位,而一個車間內至少有一個流水線。流水線的工作需要電源,電源就相當於cpu。
所以,進程只是用來把資源集中到一起(進程只是一個資源單位,或者說資源集合),而線程才是cpu上的執行單位。
多線程(即多個控制線程)的概念是,在一個進程中存在多個控制線程,多個控制線程共用該進程的地址空間,相當於一個車間內有多條流水線,都共用一個車間的資源。
創建:
線程創建方式一:
1 from multiprocessing import Process 2 from threading import Thread 3 4 5 def func(n): 6 print('xxxxx') 7 print('>>>',n) 8 9 10 if __name__ == '__main__': 11 12 # p = Process(target=func,args=(1,)) 13 # p.start() 14 15 t = Thread(target=func,args=(1,)) # 直接創建 16 t.start() 17 18 print('主線程結束')View Code
面向對象創建:
1 from threading import Thread 2 3 4 # 面向對象創建 5 class Mythread(Thread): # 繼承Thread父類 6 7 def __init__(self,xx): 8 super().__init__() 9 self.xx = xx 10 11 def run(self): # 必須有run,覆蓋父類run中的pass 12 print(self.xx) 13 print('我重置父類方法') 14 15 def func1(self): # 寫其他方法 16 print('我是func1') 17 18 19 if __name__ == '__main__': 20 21 t1 = Mythread('xx') 22 t1.start() # 預設執行run 23 t1.func1() # 調用其他方法 24 # 25 26 from multiprocessing import Process 27 from threading import ThreadView Code
2.Thread類方法
join方法:
主線程等待join子線程執行完畢後才執行
1 import time 2 from threading import Thread 3 4 5 def func(n): 6 time.sleep(n) 7 print('我是子線程') 8 9 10 if __name__ == '__main__': 11 12 t = Thread(target=func,args=(1,)) 13 t.start() 14 15 t.join() # 等待子線程執行結束 16 print('我是主線程,子線程結束再執行我')join
其他方法:
1 '' 2 Thread實例對象的方法 3 # isAlive(): 返回線程是否活動的。 4 # getName(): 返回線程名。 5 # setName(): 設置線程名。 6 7 threading模塊提供的一些方法: 8 # threading.currentThread(): 返回當前的線程變數。 9 # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啟動後、結束前,不包括啟動前和終止後的線程。 10 # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果 11 '''
1 import time, threading 2 from threading import Thread, current_thread 3 4 5 def func(): 6 time.sleep(2) 7 print('子線程,名字是:', current_thread().getName()) # 返回線程名 8 print('子線程,ID是:', current_thread().ident) # 返回線程id 9 10 11 if __name__ == '__main__': 12 13 for i in range(10): 14 t = Thread(target=func, ) 15 t.start() 16 17 print(threading.enumerate()) # 返回一個包含正在運行的list 18 print(threading.activeCount()) # 返回正在運行的線程數量,等同len(threading.enumerate()) 19 20 print('主線程結束')其他方法示例
3.守護線程、事件
守護線程:
主進程結束,守護進程跟著結束,再執行非守護進程
主線程要等待所有非守護線程運行結束才會結束(因為他們屬於同一進程)
需註意:運行結束並非終止運行
xxx.setDaemon(True) 或者 xxx.daemon = True
1 import time 2 from threading import Thread 3 from multiprocessing import Process 4 5 6 def func1(): 7 time.sleep(3) 8 print('任務1結束') 9 10 11 def func2(): 12 time.sleep(2) 13 print('任務2結束') 14 15 16 if __name__ == '__main__': 17 18 # p1 = Process(target=func1,) 19 # p2 = Process(target=func2,) 20 # # p1.daemon = True 21 # p2.daemon = True 22 # p1.start() 23 # p2.start() 24 # 25 # print('主進程結束') 26 27 t1 = Thread(target=func1,) 28 t2 = Thread(target=func2,) 29 # t1.setDaemon(True) # 只列印出 主線程和t2 因為t2時間比t1小 30 t2.setDaemon(True) # 會正常列印出所有 因為t1時間大於t2 31 t1.start() 32 t2.start() 33 34 print('主線程結束')守護線程與守護進程
事件:
event.isSet():返回event的狀態值;
event.wait():如果 event.isSet()==False將阻塞線程;
event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度;
event.clear():恢復event的狀態值為False。
1 import time 2 from threading import Event 3 4 e = Event() # 預設false狀態 5 print(e.isSet()) # 事件當前狀態 6 7 e.set() # 改變成Ture 8 print(e.isSet()) 9 10 print('稍等...') 11 # e.clear() # 將e的狀態改為False 12 e.wait() # 如果 event.isSet()==False將阻塞線程 13 14 if e.isSet() == True: 15 time.sleep(1) 16 print('滴滴滴,上車吧...')事件代碼示例
4.線程間數據問題
開啟一個線程所需要的時間要遠小於開啟一個進程
1 import time 2 from multiprocessing import Process 3 from threading import Thread 4 5 6 def func(i): 7 return '我是任務%s'%i 8 9 10 if __name__ == '__main__': 11 12 # 多線程 13 t_list = [] 14 t_start_time = time.time() 15 for i in range(10): 16 t = Thread(target=func,args=(i,)) 17 t_list.append(t) 18 t.start() 19 20 [tt.join() for tt in t_list] 21 t_end_time = time.time() 22 t_dif_time = t_end_time - t_start_time 23 24 # 多進程 25 p_list = [] 26 p_start_time = time.time() 27 for i in range(10): 28 p = Process(target=func,args=(i,)) 29 p_list.append(p) 30 p.start() 31 32 [pp.join() for pp in p_list] 33 p_end_time = time.time() 34 p_dif_time = p_end_time - p_start_time 35 # 結果受cpu影響 36 37 print('多線程耗時:',t_dif_time) 38 print('多進程耗時:',p_dif_time) 39 print('主線程結束') 40 41 ''' 42 多線程耗時: 0.0020008087158203125 43 多進程耗時: 0.4188823699951172 44 '''多線程和多進程效率對比
線程之間共用進程資源(全局變數在多個線程之間共用),但也會導致數據不但全問題
1 import time 2 from threading import Thread 3 4 num = 100 5 6 def func(): 7 global num 8 tep = num # tep替換 模擬多步操作 9 time.sleep(0.001) # 模擬延遲 10 tep -= 1 11 num = tep 12 13 14 if __name__ == '__main__': 15 16 t_list = [] 17 for i in range(100): 18 t = Thread(target=func,) 19 t_list.append(t) 20 t.start() 21 22 [tt.join() for tt in t_list] 23 24 print('主線程的num',num) # 列印出不是100可知數據是共用的 25 26 # 理論上應該是0,但多線程是併發執行的,會出現上一個線程還在運算中時,下一個線程並未等待它返回值 27 # 也拿了原來的值進來運算,所以列印出了91,92,93不等,可知多線程數據是不安全的 28 ''' 29 主線程的num 92 30 '''驗證多線程之間數據共用 數據不安全問題
加鎖解決多線程數據不安全問題
1 import time 2 from threading import Thread,Lock 3 4 num = 100 5 def func(lock,i): 6 global num 7 lock.acquire() # 加鎖 8 9 tep = num 10 time.sleep(0.001) # 模擬延遲 11 tep -= 1 12 num = tep 13 14 lock.release() # 釋放 15 16 17 if __name__ == '__main__': 18 t_list = [] 19 lock = Lock() 20 for i in range(100): 21 t = Thread(target=func,args=(lock,i)) 22 t_list.append(t) 23 t.start() 24 25 [tt.join() for tt in t_list] 26 print('主線程num',num) 27 28 ''' 29 主線程num 0 30 '''Lock
信號量Semaphore
1 import time,random 2 from threading import Thread,Semaphore 3 4 5 def func(i,s): 6 s.acquire() 7 print('%s張燒餅'%i) 8 time.sleep(random.randint(1,3)) 9 s.release() 10 # 出來一個進去一個 始終6個 最後不足6個就都進來了 11 12 13 if __name__ == '__main__': 14 s = Semaphore(6) # 與Lock類似,不過可限制最大連接數,如這裡同時只有6個線程可以獲得semaphore 15 for i in range(28): 16 t = Thread(target=func,args=(i,s,)) 17 t.start()Semaphore
5.死鎖和遞歸鎖
死鎖現象:有多個鎖時,雙方互相等待對方釋放對方手裡拿到的那個鎖導致死鎖
1 import time 2 from threading import Thread,Lock 3 4 5 def func1(lock_A,lock_B): 6 lock_A.acquire() 7 print('張全蛋拿到了A鎖') 8 time.sleep(0.5) 9 lock_B.acquire() 10 print('張全蛋拿到了B鎖') 11 lock_B.release() 12 lock_A.release() 13 14 15 def func2(lock_A,lock_B): 16 lock_B.acquire() 17 print('趙二狗拿到了B鎖') 18 lock_A.acquire() 19 print('趙二狗拿到了A鎖') 20 lock_A.release() 21 lock_B.release() 22 23 24 if __name__ == '__main__': 25 26 lock_A = Lock() 27 lock_B = Lock() 28 t1 = Thread(target=func1,args=(lock_A,lock_B,)) 29 t2 = Thread(target=func2,args=(lock_A,lock_B,)) 30 t1.start() 31 t2.start()死鎖現象
遞歸鎖:RLock
RLock管理一個內置的計數器,
每當調用acquire()時內置計數器-1;
調用release() 時內置計數器+1;
計數器不能小於0;當計數器為0時,acquire()將阻塞線程直到其他線程調用release()。
1 # 遞歸鎖解決死鎖現象 2 import time 3 from threading import Thread,Lock,RLock 4 5 6 def func1(lock_A,lock_B): 7 lock_A.acquire() 8 print('張全蛋拿到了A鎖') 9 time.sleep(0.5) 10 lock_B.acquire() 11 print('張全蛋拿到了B鎖') 12 lock_B.release() 13 lock_A.release() 14 15 16 def func2(lock_A,lock_B): 17 lock_B.acquire() 18 print('趙二狗拿到了B鎖') 19 lock_A.acquire() 20 print('趙二狗拿到了A鎖') 21 lock_A.release() 22 lock_B.release() 23 24 25 if __name__ == '__main__': 26 27 # lock_A = Lock() 28 # lock_B = Lock() 29 lock_A = lock_B = RLock() 30 t1 = Thread(target=func1,args=(lock_A,lock_B,)) 31 t2 = Thread(target=func2,args=(lock_A,lock_B,)) 32 t1.start() 33 t2.start()遞歸鎖解決死鎖現象