日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

解析Python中的线程与进程

發布時間:2025/3/20 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 解析Python中的线程与进程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1 基礎知識

現在的 PC 都是多核的,使用多線程能充分利用 CPU 來提供程序的執行效率。

1.1 線程

線程是一個基本的 CPU 執行單元。它必須依托于進程存活。一個線程是一個execution context(執行上下文),即一個 CPU 執行時所需要的一串指令。

1.2 進程

進程是指一個程序在給定數據集合上的一次執行過程,是系統進行資源分配和運行調用的獨立單位。可以簡單地理解為操作系統中正在執行的程序。也就說,每個應用程序都有一個自己的進程。
每一個進程啟動時都會最先產生一個線程,即主線程。然后主線程會再創建其他的子線程。

1.3 兩者的區別

  • 線程必須在某個進程中執行。
  • 一個進程可包含多個線程,其中有且只有一個主線程。
  • 多線程共享同個地址空間、打開的文件以及其他資源。
  • 多進程共享物理內存、磁盤、打印機以及其他資源。

1.4 線程的類型

線程的因作用可以劃分為不同的類型。大致可分為:

  • 主線程
  • 子線程
  • 守護線程(后臺線程)
  • 前臺線程

2 Python 多線程

2.1 GIL

其他語言,CPU 是多核時是支持多個線程同時執行。但在 Python 中,無論是單核還是多核,同時只能由一個線程在執行。其根源是 GIL 的存在。

GIL 的全稱是 Global Interpreter Lock(全局解釋器鎖),來源是 Python 設計之初的考慮,為了數據安全所做的決定。某個線程想要執行,必須先拿到 GIL,我們可以把 GIL 看作是“通行證”,并且在一個 Python 進程中,GIL 只有一個。拿不到通行證的線程,就不允許進入 CPU 執行。

而目前 Python 的解釋器有多種,例如:

  • CPython:CPython 是用C語言實現的 Python 解釋器。 作為官方實現,它是最廣泛使用的 Python 解釋器。

  • PyPy:PyPy 是用RPython實現的解釋器。RPython 是 Python 的子集, 具有靜態類型。這個解釋器的特點是即時編譯,支持多重后端(C, CLI, JVM)。PyPy 旨在提高性能,同時保持最大兼容性(參考CPython 的實現)。

  • Jython:Jython 是一個將 Python 代碼編譯成 Java 字節碼的實現,運行在JVM (Java Virtual Machine) 上。另外,它可以像是用 Python 模塊一樣,導入 并使用任何Java類。

  • IronPython:IronPython 是一個針對 .NET 框架的 Python 實現。它 可以用 Python 和 .NET framewor k的庫,也能將 Python 代碼暴露給 .NET 框架中的其他語言。

GIL 只在 CPython 中才有,而在 PyPy 和 Jython 中是沒有 GIL 的。

每次釋放 GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。這就導致打印線程執行時長,會發現耗時更長的原因。

并且由于 GIL 鎖存在,Python 里一個進程永遠只能同時執行一個線程(拿到 GIL 的線程才能執行),這就是為什么在多核CPU上,Python 的多線程效率并不高的根本原因。

2.2 創建多線程

Python提供兩個模塊進行多線程的操作,分別是thread和threading,前者是比較低級的模塊,用于更底層的操作,一般應用級別的開發不常用。

直接使用threading.Thread()

import threading# 這個函數名可隨便定義 def run(n):print("current task:", n)if __name__ == "__main__":t1 = threading.Thread(target=run, args=("thread 1",))t2 = threading.Thread(target=run, args=("thread 2",))t1.start()t2.start()

也可以通過繼承threading.Thread類重寫run方法來自定義線程類

''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import threadingclass MyThread(threading.Thread):def __init__(self, n):super(MyThread, self).__init__() # 重構run函數必須要寫self.n = ndef run(self):print("current task:", n)if __name__ == "__main__":t1 = MyThread("thread 1")t2 = MyThread("thread 2")t1.start()t2.start()

2.3 線程合并

Join函數執行順序是逐個執行每個線程,執行完畢后繼續往下執行。主線程結束后,子線程還在運行,join函數使得主線程等到子線程結束時才退出。

import threadingdef count(n):while n > 0:n -= 1if __name__ == "__main__":t1 = threading.Thread(target=count, args=("100000",))t2 = threading.Thread(target=count, args=("100000",))t1.start()t2.start()# 將 t1 和 t2 加入到主線程中t1.join()t2.join()

2.4 線程同步與互斥鎖

線程之間數據共享的。當多個線程對某一個共享數據進行操作時,就需要考慮到線程安全問題。threading模塊中定義了Lock 類,提供了互斥鎖的功能來保證多線程情況下數據的正確性。

用法的基本步驟:

#創建鎖 mutex = threading.Lock() #鎖定 mutex.acquire([timeout]) #釋放 mutex.release()

其中,鎖定方法acquire可以有一個超時時間的可選參數timeout。如果設定了timeout,則在超時后通過返回值可以判斷是否得到了鎖,從而可以進行一些其他的處理。
具體用法見示例代碼:

''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import threading import timenum = 0 mutex = threading.Lock()class MyThread(threading.Thread):def run(self):global numtime.sleep(1)if mutex.acquire(1): num = num + 1msg = self.name + ': num value is ' + str(num)print(msg)mutex.release()if __name__ == '__main__':for i in range(5):t = MyThread()t.start()

2.5 可重入鎖(遞歸鎖)

為了滿足在同一線程中多次請求同一資源的需求,Python 提供了可重入鎖(RLock)。
RLock內部維護著一個Lock和一個counter變量,counter 記錄了 acquire 的次數,從而使得資源可以被多次 require。直到一個線程所有的 acquire 都被 release,其他的線程才能獲得資源。
具體用法如下:

#創建 RLock mutex = threading.RLock()class MyThread(threading.Thread):def run(self):if mutex.acquire(1):#第一次acquire鎖print("thread " + self.name + " get mutex")time.sleep(1)mutex.acquire()#第二次acquire鎖mutex.release()#第一次release鎖mutex.release()#第二次release鎖

2.6 守護線程

如果希望主線程執行完畢之后,不管子線程是否執行完畢都隨著主線程一起結束。我們可以使用setDaemon(bool)函數,它跟join函數是相反的。它的作用是設置子線程是否隨主線程一起結束,必須在start() 之前調用,默認為False。

2.7 定時器

如果需要規定函數在多少秒后執行某個操作,需要用到Timer類。具體用法如下:

''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from threading import Timerdef show():print("Pyhton")# 指定一秒鐘之后執行 show 函數 t = Timer(1, show) t.start()

3 Python 多進程

3.1 創建多進程

Python 要進行多進程操作,需要用到muiltprocessing庫,其中的Process類跟threading模塊的Thread類很相似。所以直接看代碼熟悉多進程。

直接使用Process, 代碼如下:

from multiprocessing import Process def show(name):print("Process name is " + name)if __name__ == "__main__":proc = Process(target=show, args=('subprocess',)) proc.start() proc.join()

繼承Process來自定義進程類,重寫run方法, 代碼如下:

from multiprocessing import Process import timeclass MyProcess(Process):def __init__(self, name):super(MyProcess, self).__init__()self.name = namedef run(self):print('process name :' + str(self.name))time.sleep(1)if __name__ == '__main__':for i in range(3):p = MyProcess(i)p.start()for i in range(3):p.join()

3.2 多進程通信

進程之間不共享數據的。如果進程之間需要進行通信,則要用到Queue模塊或者Pipi模塊來實現。

Queue

Queue 是多進程安全的隊列,可以實現多進程之間的數據傳遞。它主要有兩個函數,put和get。

put() 用以插入數據到隊列中,put 還有兩個可選參數:blocked 和 timeout。如果 blocked 為 True(默認值),并且 timeout 為正值,該方法會阻塞 timeout 指定的時間,直到該隊列有剩余的空間。如果超時,會拋出 Queue.Full 異常。如果 blocked 為 False,但該 Queue 已滿,會立即拋出 Queue.Full 異常。

get()可以從隊列讀取并且刪除一個元素。同樣,get 有兩個可選參數:blocked 和 timeout。如果 blocked 為 True(默認值),并且 timeout 為正值,那么在等待時間內沒有取到任何元素,會拋出 Queue.Empty 異常。如果blocked 為 False,有兩種情況存在,如果 Queue 有一個值可用,則立即返回該值,否則,如果隊列為空,則立即拋出 Queue.Empty 異常。

具體用法如下:

''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from multiprocessing import Process, Queuedef put(queue):queue.put('Queue 用法')if __name__ == '__main__':queue = Queue()pro = Process(target=put, args=(queue,))pro.start()print(queue.get()) pro.join()

Pipe

Pipe的本質是進程之間的用管道數據傳遞,而不是數據共享,這和socket有點像。pipe() 返回兩個連接對象分別表示管道的兩端,每端都有send() 和recv()函數。

如果兩個進程試圖在同一時間的同一端進行讀取和寫入那么,這可能會損壞管道中的數據。

具體用法如下:

from multiprocessing import Process, Pipedef show(conn):conn.send('Pipe 用法')conn.close()if __name__ == '__main__':parent_conn, child_conn = Pipe()pro = Process(target=show, args=(child_conn,))pro.start()print(parent_conn.recv()) pro.join()

3.3 進程池

創建多個進程,我們不用傻傻地一個個去創建。我們可以使用Pool模塊來搞定。

Pool 常用的方法如下:

具體用法見示例代碼:

''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from multiprocessing import Pool def show(num):print('num : ' + str(num))if __name__=="__main__":pool = Pool(processes = 3)for i in range(6):# 維持執行的進程總數為processes,當一個進程執行完畢后會添加新的進程進去pool.apply_async(show, args=(i, )) print('====== apply_async ======')pool.close()#調用join之前,先調用close函數,否則會出錯。執行完close后不會有新的進程加入到pool,join函數等待所有子進程結束pool.join()

4 選擇多線程還是多進程?

在這個問題上,首先要看下你的程序是屬于哪種類型的。一般分為兩種 CPU 密集型 和 I/O 密集型。

CPU 密集型:程序比較偏重于計算,需要經常使用 CPU 來運算。例如科學計算的程序,機器學習的程序等。
I/O 密集型:顧名思義就是程序需要頻繁進行輸入輸出操作。爬蟲程序就是典型的 I/O 密集型程序。

如果程序是屬于 CPU 密集型,建議使用多進程。而多線程就更適合應用于 I/O 密集型程序。

總結

以上是生活随笔為你收集整理的解析Python中的线程与进程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。