python中的多工
阿新 • • 發佈:2018-12-06
多工
多工 什麼是任務 一個電腦執行這的軟體 什麼是多工 電腦同時執行著的多個軟體 多工原理 時間片的輪轉 並行與併發 併發:假的多工,多個任務共用一個核 並行:正的多工,一個核處理一個程式 生理過程(從生到死) 建立 -> 就緒 -> 執行 -> 阻塞 -> 死亡 執行緒和程序的建立一定要在主函式中,且主任務和子任務一起往下執行,遇到join()方法,主任務會等子任務執行完在結束View Code
執行緒
執行緒 特點 檢視正在執行的執行緒列表 threading.enumerate() 只要程式已啟動,Python直譯器就會自動建立一個主執行緒 主執行緒等待其他執行緒結束後在結束 執行緒target指向的函式執行完畢,執行緒就結束了 子執行緒是呼叫start()之後開啟的 多個執行緒共享全域性變數 建立 通過繼承的方式建立執行緒 特點:寫法複雜,使用簡單,可以使用物件的特性(封裝、繼承、多型) 業務邏輯比較複雜時使用 方式一View Codeimport threading p = threading.THread(target=函式名,args=(1,)) p.start() p.join() 方式二 import threading class MyThread(threading.Thread): def run(): 子執行緒要做的事情 t= MyThread() t.start() t.join() 互斥鎖 -->科學家吃麵(筷子和碗) 1.什麼是資源競爭? 多個執行緒爭搶做同一件事 2.資源競爭會帶來什麼問題? 多執行緒共享同一個資源的時候,當操作這個資源(變數)足夠多的次數時,可能會出現問題 eg:1000000次的累加 3.如何解決資源競爭的問題? 互斥鎖 4.什麼是互斥鎖? 確保了某段關鍵程式碼只能由一個執行緒從頭到尾完整地執行 5.如何使用互斥鎖? 1.建立鎖物件 mutex = threading.Lock() 2.獲取鎖 mutex.acquire() 3.釋放鎖 mutex.release() 6.原則: 存在資源競爭的程式碼 加鎖的程式碼越少越少 7.死鎖 什麼是死鎖? 執行緒1等待執行緒2釋放鎖,執行緒2等待執行緒1釋放鎖 如何解決死鎖? 1.設計寫程式碼的時候就避免 2.設定超時時間 3.銀行家演算法
程序
程序 1.程式: 就是程式設計師寫的程式碼,沒有執行起來的程式碼 2.程序 執行起來的程式,程式碼+計算機資源 程序是實現多工的第二種方式 3.程式執行起來之後,Python直譯器會給程式建立一個程序,叫做主程序 特點 1.檢視程序 windows: 工作管理員 linux:ps -aux 殺死程序 kill -9 PID top htop 相當於windows中的工作管理員 2.程序的寫實拷貝 寫(修改)的時候拷貝一份 程序不共享全域性變數 通過args給程序傳遞資料 3.獲取程序id和父程序的id os.getpid() os.getppid() 4.主程序等待子程序結束後再結束(主程序替子程序收屍),程序的執行順便不確定 建立 方式一 import multiprocessing p = multiprocessing.Process(target=函式名,args=(1,)) p.start() p.join() 方式二 import multiprocessing class MyProcess(multiprocessing.Process): def run(): print("子執行緒要做的事情") p = MyProcess() p.start() p.join() 程序間通訊(傳遞資料) 預設情況下,程序之間不能互相訪問資料 佇列(Queue) 常用的方法 get()/put()/full() 每個程序都可以往這個佇列中寫資料,都可以從這個佇列中讀取資料 編碼步驟: 建立佇列物件 給佇列中放資料 從佇列中取資料 程序池(Pool) 什麼是程序池? 一次性在程序池中建立多個程序 程序的作用? 減少了銷燬執行緒的次數,從而提高效率 如何使用程序池? 建立程序池物件 呼叫方法完成任務 from multiprocessing import Pool import os, time, random def worker(msg): t_start = time.time() print("%s開始執行,程序號為%d" % (msg, os.getpid())) # random.random()隨機生成0~1之間的浮點數 time.sleep(random.random() * 2) t_stop = time.time() print(msg, "執行完畢,耗時%0.2f" % (t_stop - t_start)) def main(): po = Pool(3) # 定義一個程序池,最大程序數3 for i in range(0, 10): # Pool().apply_async(要呼叫的目標,(傳遞給目標的引數元祖,)) # 每次迴圈將會用空閒出來的子程序去呼叫目標 po.apply_async(worker, (i,)) print("----start----") po.close() # 關閉程序池,關閉後po不再接收新的請求 time.sleep(1) # po.join() # 等待po中所有子程序執行完成,必須放在close語句之後 print("-----end-----") if __name__ == '__main__': main()View Code
協程
協程 什麼是可迭代物件 一個普通的物件實現了iter內建函式 什麼是迭代器 一個普通的物件實現了iter和next內建函式 迭代器的特點 儲存的是生成資料的方式,而不直接儲存資料 好處:節省系統空間 什麼是生成器 它是一個特殊的迭代器 yield 一個普通的函式裡面寫了yield的話,他就是一個生成器模板 執行函式遇到yield會阻塞 呼叫next()或者send()會解阻塞 send()可以用來傳遞引數 eg: def func(all_num): a, b = 0, 1 count_num = 0 while True: if count_num < all_num: result = yield a # 如果一個函式中有yield,則說明這是特殊函式,叫生成器的模板 print(">>>>ret>>>>", result) a, b = b, a + b count_num += 1 else: raise StopIteration f = func(10) ret = next(f) print(ret) ret = f.send("hahaha") # 將這個結果傳遞給 result = yield a 讓result來儲存"hahaha" ret1 = next(f) print(ret1) # 結果為None傳遞一次send後,後面的資料都需要send來傳輸,否則結果為None ret2 = f.send("ok") 利用yield做協程 寫多個函式,每個函式中都寫yield,函式執行時遇到yield就會阻塞 然後交替著呼叫不同任務的next()方法,這樣就用協程實現了多工 原理: 利用執行緒的空閒時間去執行其他的任務 特點: 協程依賴於執行緒,執行緒依賴程序 從系統開銷講,程序>執行緒>協程 建立協程 yield next() send() import gevent import time from gevent import monkey monkey.patch_all() def func1(num): for i in range(num): print(gevent.getcurrent(), i) time.sleep(0.5) def func2(num): for i in range(num): print(gevent.getcurrent(), i) time.sleep(0.5) def func3(num): for i in range(num): print(gevent.getcurrent(), i) time.sleep(0.5) def func4(num): for i in range(num): print(gevent.getcurrent(), i) time.sleep(0.5) def func5(num): for i in range(num): print(gevent.getcurrent(), i) time.sleep(0.5) def main(): gevent.joinall([gevent.spawn(func1, 10), gevent.spawn(func2, 10), gevent.spawn(func3, 10), gevent.spawn(func4, 10), gevent.spawn(func5, 10) ]) if __name__ == "__main__": main()View Code