網站首頁 編程語言 正文
一、什么是進程和線程?
進程是分配資源的最小單位,線程是系統調度的最小單位。
當應用程序運行時最少會開啟一個進程,此時計算機會為這個進程開辟獨立的內存空間,不同的進程享有不同的空間,而一個CPU在同一時刻只能夠運行一個進程,其他進程處于等待狀態。
一個進程內部包括一個或者多個線程,這些線程共享此進程的內存空間與資源。相當于把一個任務又細分成若干個子任務,每個線程對應一個子任務。
二、多進程和多線程?
對于一個CPU來說,在同一時刻只能運行一個進程或者一個線程,而單核CPU往往是在進程或者線程間切換執行,每個進程或者線程得到一定的CPU時間,由于切換的速度很快,在我們看來是多個任務在并行執行(同一時刻多個任務在執行),但實際上是在并發執行(一段時間內多個任務在執行)。
單核CPU的并發往往涉及到進程或者線程的切換,進程的切換比線程的切換消耗更多的時間與資源。在單核CPU下,CPU密集的任務采用多進程或多線程不會提升性能,而在IO密集的任務中可以提升(IO阻塞時CPU空閑)。
而多核CPU就可以做到同時執行多個進程或者多個進程,也就是并行運算。在擁有多個CPU的情況下,往往使用多進程或者多線程的模式執行多個任務。
三、python中的多進程和多線程
1、多進程
def Test(pid): ? ? print("當前進程{}:{}".format(pid, os.getpid())) ? ? for i in range(1000000000): ? ? ? ? pass if __name__ == '__main__': ? ? #單進程 ? ? start = time.time() ? ? for i in range(2): ? ? ? ? Test(i) ? ? end = time.time() ? ? print((end - start))
單進程輸出結果如圖:
def Test(pid): ? ? print("當前子進程{}:{}".format(pid, os.getpid())) ? ? for i in range(100000000): ? ? ? ? pass if __name__ == '__main__': ? ? #多進程 ? ? print("父進程:{}".format(os.getpid())) ? ? start = time.time() ? ? pool = Pool(processes=2) ? ? pid = [i for i in range(2)] ? ? pool.map(Test, pid) ? ? pool.close() ? ? pool.join() ? ? end = time.time() ? ? print((end - start))
多進程輸出結果如圖:
從輸出結果可以看出都是執行兩次for循環,多進程比單進程減少了近乎一半的時間(這里使用了兩個進程),并且查看CPU情況可以看出多進程利用了多個CPU。
python中的多進程可以利用mulitiprocess
模塊的Pool類創建,利用Pool的map方法來運行子進程。
一般多進程的執行如下代碼:
def Test(pid): ? ? print("當前子進程{}:{}".format(pid, os.getpid())) ? ? for i in range(100000000): ? ? ? ? pass if __name__ == '__main__': ? ? #多進程 ? ? print("父進程:{}".format(os.getpid())) ? ? pool = Pool(processes=2) ? ? pid = [i for i in range(4)] ? ? pool.map(Test, pid) ? ? pool.close() ? ? pool.join()
1、利用Pool類創建一個進程池,processes
聲明在進程池中最多可以運行幾個子進程,不聲明的情況下會自動根據CPU數量來設定,原則上進程池容量不超過CPU數量。(出于資源的考慮,不要創建過多的進程)
2、聲明一個可迭代的變量,該變量的長度決定要執行多少次子進程。
3、利用map()方法執行多進程,map方法兩個參數,第一個參數是多進程執行的方法名,第二個參數是第二步聲明的可迭代變量,里面的每一個元素是方法所需的參數。 這里需要注意幾個點:1)進程池滿的時候請求會等待,以上述代碼為例,聲明了一個容量為2的進程池,但是可迭代變量有4個,那么在執行的時候會先創建兩個子進程,此時進程池已滿,等待有子進程執行完成,才繼續處理請求;
2) 子進程處理完一個請求后,會利用已經創建好的子進程繼續處理新的請求而不會重新創建進程。
從圖3可以看出上述兩個點,如果同時處理4個進程,那么只需要2秒鐘,這里是分成兩次處理,花費了4秒,并且兩次處理使用的子進程號都相同。
3)map會將每個子進程的返回值匯總成一個列表返回。
4、在所有請求處理結束后使用close()方法關閉進程池不再接受請求。
5、使用join()方法讓主進程阻塞,等待子進程退出,join()
方法要放在close()
方法之后,防止主進程在子進程結束之前退出。
2、多線程
python的多線程模塊用threading類進行創建
import time import threading import os count = 0 def change(n): ? ? global count ? ? count = count + n ? ? count = count - n def run(n): ? ? print("當前子線程:{}".format(threading.current_thread().name)) ? ? for i in range(10000000): ? ? ? ? change(n) if __name__ == '__main__': ? ? print("主線程:{}".format(threading.current_thread().name)) ? ? thread_1 = threading.Thread(target=run, args=(3,)) ? ? thread_2 = threading.Thread(target=run, args=(10,)) ? ? thread_1.start() ? ? thread_2.start() ? ? thread_1.join() ? ? thread_2.join() ? ? print(count)
程序執行會創建一個進程,進程會默認啟動一個主線程,使用threading.Thread()創建子線程;target為要執行的函數;args傳入函數需要的參數;start()啟動子線程,join()阻塞主線程先運行子線程。 由于變量由多個線程共享,任何一個線程都可以對于變量進行修改,如果同時多個線程修改變量就會出現錯誤。
上面的程序在理論上的結果應該為0,但運行結果如圖:
出現這個結果的原因就是多個線程同時對于變量修改,在賦值時出現錯誤,具體解釋見多線程
解決這個問題就是在修改變量的時候加鎖,這樣就可以避免出現多個線程同時修改變量。
import time import threading import os count = 0 lock = threading.Lock() def change(n): ? ? global count ? ? count = count + n ? ? count = count - n def run(n): ? ? print("當前子線程:{}".format(threading.current_thread().name)) ? ? for i in range(10000000): ? ? ? ? # lock.acquire() ? ? ? ? # try: ? ? ? ? ? ? change(n) ? ? ? ? # finally: ? ? ? ? # ? ? lock.release() if __name__ == '__main__': ? ? print("主線程:{}".format(threading.current_thread().name)) ? ? thread_1 = threading.Thread(target=run, args=(3,)) ? ? thread_2 = threading.Thread(target=run, args=(10,)) ? ? thread_1.start() ? ? thread_2.start() ? ? thread_1.join() ? ? thread_2.join() ? ? print(count)
python中的線程需要先獲取GIL(Global Interpreter Lock)鎖才能繼續運行,每一個進程僅有一個GIL,線程在獲取到GIL之后執行100字節碼或者遇到IO中斷時才會釋放GIL,這樣在CPU密集的任務中,即使有多個CPU,多線程也是不能夠利用多個CPU來提高速率,甚至可能會因為競爭GIL導致速率慢于單線程。所以對于CPU密集任務往往使用多進程,IO密集任務使用多線程。
原文鏈接:https://blog.csdn.net/m0_67575344/article/details/124408744
相關推薦
- 2022-01-29 寶塔Linux面板的ftp無法使用連接解決方案
- 2022-07-07 C++如何將二叉搜索樹轉換成雙向循環鏈表(雙指針或數組)_C 語言
- 2022-04-03 用Python實現屏幕截圖詳解_python
- 2023-03-11 Python基礎教程之增加和去除數字的千位分隔符_python
- 2022-03-23 安裝tomcat后可能出現的問題介紹_Tomcat
- 2022-01-18 VSCode git拉取代碼,提示:在簽出前,請清理存儲庫工作樹。
- 2022-01-03 當前時間與新年倒計時
- 2023-10-12 v-if和v-for的優先級以及二者同時使用的情況
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支