python中執行緒的知識點
python中執行緒的知識點
-
什麼是執行緒?
程式的執行線路。每個程序預設有一條執行緒。執行緒包含了程式的具體步驟。
多執行緒就是一個程序中有除主執行緒(預設執行緒)外還有多個執行緒。
-
執行緒與程序的關係(程序包含執行緒,而執行緒依賴程序存在)
1.程序包含了執行該程式的所有資源,是一個資源單位。
2.執行緒是CPU的執行單位(最小的執行單位)。
3.程序一旦被建立,就預設開啟了一條執行緒,稱之為主執行緒。
4.執行緒共享建立它的程序的地址空間;程序有自己的地址空間。
5.執行緒可以直接訪問其程序的資料段;程序有它們自己的父程序的資料段副本。
6.執行緒可以直接與程序的其他執行緒通訊;程序必須使用程序間通訊來與兄弟程序通訊。
7.容易建立新執行緒;新程序需要父程序的複製。
8.執行緒可以對同一程序的執行緒進行相當大的控制;流程只能對子流程進行控制。
9.對主執行緒的更改(取消、優先順序更改等)可能會影響程序中其他執行緒的行為;對父程序的更改不會影響子程序。
-
為什麼使用多執行緒?
為了提高程式執行效率。與程序區別是執行緒對於系統資源的佔用非常小。
1.多個執行緒共享一個程序的地址空間。
2.執行緒比程序對系統資源佔用小,建立速度快10-100倍。
3.同一個程序中多個執行緒之間資源共享,不需要像程序一樣需要程序間通訊。
-
執行緒的兩種開啟方式
#第一種方式:匯入threading模組中的Thread類來建立一個物件 from threading import Thread def task(): print('子執行緒 running。。。') t = Thread(target=task) t.start() print('over') # from threading import Thread # # def task(): #print('子執行緒 running。。。') # # if __name__ == '__main__': #t = Thread(target=task) #t.start() #print('over') #第二種方式:建立一個子類繼承Thread類,可自定義run方法,但是不能改變run方法名。 from threading import Thread class MyThread(Thread): def __init__(self, name): super().__init__() self.name = name def run(self): print('%s say hi' % self.name) if __name__ == '__main__': t = MyThread('daidai') t.start()
-
程序和執行緒的對比
from multiprocessing import Process from threading import Thread import time def task(): pass #開啟100個執行緒花費的時間 start_time = time.time() ts = [] for i in range(100): t = Thread(target=task) t.start() ts.append(t) print(os.getpid()) # 所有的程序編號都一樣,同屬一個程序中 for t in ts: t.join() print(time.time()-start_time) ### 0.018949270248413086 #開啟100個程序花費的時間 if __name__ == '__main__': start = time.time() ps = [] for i in range(100): p = Process(target=task) p.start() ps.append(p) for p in ps: p.join() print(time.time()-start) ### 5.281934499740601
-
執行緒間的資源共享
from threading import Thread x = 100 def task(): print("run....") global x# 修改全域性變數 x = 0 t = Thread(target=task) t.start() t.join() # join方法將子執行緒的優先順序提高到主執行緒前 print(x) print("over") #### run.... 0 over
-
守護執行緒
無論是程序還是執行緒:都遵循守護一方等待主方執行完畢後被銷燬。執行完畢不是終止執行。
1.對於主程序來說,執行完畢是主程式碼執行完畢。
2.對於主執行緒來說,執行完畢是主執行緒內所有非守護執行緒執行完畢。所以守護執行緒會在所有非守護執行緒結束後結束。
詳細解釋:
#1.主程序在其程式碼結束後就已經算執行完畢(守護程序在此時會被回收),然後著京城會一直等著非守護的子程序都執行完畢後回收子程序的資源(否則就會產生殭屍程序),才會結束。
#2.主執行緒在其他非守護執行緒執行完畢後才算執行完畢(守護執行緒在此時就會被回收)。因為主執行緒的結束意味著程序的結束,程序整體的資源都將被回收,而程序必須保證非守護執行緒都執行完畢後才能結束。
from threading import Thread import time def task(): print('sub thread running') time.sleep(3) print('sub thread over') t = Thread(target=task) t.setDaemon(True) # 要在開啟之前設定 t.start() print('main thread over') ### sub thread running main thread over
-
Thread類物件常用的屬性和方法
物件的方法:
isAlive():返回執行緒是否活動
getName():返回執行緒名
setName():設定執行緒名
threading模組的一些方法:
current_thread():返回當前執行緒變數,獲取當前執行緒
enumerate():返回一個包含正在執行執行緒的列表
active_count():返回正在進行的執行緒數量,與len(threading.enumerate())有相同的結果。
from threading import Thread,current_thread,enumerate,active_count import time import os def task(): time.sleep(3) print(current_thread().getName()) t = Thread(target = task) t.start() print(current_thread().getName()) print(current_thread()) print(enumerate()) print(active_count()) print('daidai') #執行結果 MainThread <_MainThread(MainThread, started 14652)> [<_MainThread(MainThread, started 14652)>, <Thread(Thread-1, started 15548)>] 2 daidai Thread-1 t.join()#主執行緒等待子執行緒結束
-
執行緒互斥鎖
當多個程序或者多個執行緒需要同時修改同一份資料時,可能造成資料的錯亂,所以需要給說句加上鎖。
同樣的執行緒中也有死鎖和可重入鎖RLock
import time from threading import Thread, Lock lock = Lock() a = 100 def task(): lock.acquire() global a# 修改全域性變數a temp = a-1 time.sleep(0.05) a = temp lock.release() ts = [] for i in range(100): t = Thread(target=task) t.start() ts.append(t) for t in ts: t.join() print(a)# 全域性中的a已經被修改為0
-
訊號量Semaphore
semaphore管理一個內建的計數器,每當呼叫acquire()時內建計數器-1:
呼叫release()時內建計數器+1;
計數器不能小於0;當計數器為0時,acquire()將阻塞執行緒直到其他執行緒呼叫release()。
訊號量其實也是一種鎖,特點是可以設定一個數據可以被幾個執行緒(程序)共享。
與普通鎖的區別:
普通鎖一旦加鎖就意味著這個資料在同一時間只能被一個執行緒或程序使用。
訊號量可以讓資料在同一時間能被多個執行緒使用。
#例項:開啟10個執行緒,每次執行3個 from threading import Semaphore, Thread, current_thread import time sem = Semaphore(3) def task(): sem.acquire() print('%s task running' % current_thread()) time.sleep(3) sem.release() for i in range(10): t = Thread(target=task) t.start()
-
生產者消費者模型中的JoinableQueue
JoinableQueue類是一種佇列,Queue的子類。但是例項化的物件可以明確直到佇列中是否有資料及資料的使用量。
引數介紹:
maxsize是佇列中允許最大項數,省略則無大小限制。
方法介紹:
JoinableQueue的例項除了與Queue例項物件相同方法之外還有:
task_done():使用者使用此方法發出訊號,表示q.get()的返回專案已經被處理。如果呼叫此方法的次數大於從佇列中刪除專案的數量,將引發異常。
q.join():明確生產者不會再生產資料。加入資料到佇列中。
import time import random from multiprocessing import Process, JoinableQueue def eat_hotdog(name, q): while True: res = q.get() if not res: print('吃完了。。。') break print('%s吃了%s' % (name, res)) time.sleep(random.randint(1, 2)) q.task_done() #向q.join()傳送一次訊號,證明一個數據已經被取走了 def make_hotdog(name, q): for i in range(1,6): time.sleep(random.randint(1, 2)) print('%s 生產了第%s個熱狗' % (name,i)) res = '%s的%s個熱狗'% (name,i) q.put(res) if __name__ == '__main__': q = JoinableQueue() #生產者1 c1 = Process(target=make_hotdog, args=('萬達', q)) c1.start() #生產者2 c2 = Process(target=make_hotdog, args=('呆呆', q)) c2.start() p2 = Process(target=eat_hotdog, args=('思聰', q)) p2.daemon = True p2.start() # 首先保證生產者全部產完成 c1.join() c2.join() # 保證佇列中的資料全部被處理了 q.join()# 明確生產方已經不會再生成資料了
posted on2019-03-26 00:10 小錦毛 閱讀(... ) 評論(... )編輯