人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動態(tài)

分析詳解python多線程與多進程區(qū)別

發(fā)布日期:2022-01-21 10:40 | 文章來源:源碼中國

python的多線程比較雞肋,優(yōu)先使用多進程

1 基礎知識

現(xiàn)在的 PC 都是多核的,使用多線程能充分利用 CPU 來提供程序的執(zhí)行效率。

1.1 線程

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

1.2 進程

進程是指一個程序在給定數(shù)據(jù)集合上的一次執(zhí)行過程,是系統(tǒng)進行資源分配和運行調(diào)用的獨立單位??梢院唵蔚乩斫鉃椴僮飨到y(tǒng)中正在執(zhí)行的程序。也就說,每個應用程序都有一個自己的進程。

每一個進程啟動時都會最先產(chǎn)生一個線程,即主線程。然后主線程會再創(chuàng)建其他的子線程

1.3 兩者的區(qū)別

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

2 Python 多進程

2.1 創(chuàng)建多進程

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

方法1:直接使用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()

方法2:繼承Process來自定義進程類,重寫run方法

代碼如下:

from multiprocessing import Process
import time
class MyProcess(Process):
 def __init__(self, name):
  super(MyProcess, self).__init__()
  self.name = name
 def 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()

2.2 多進程通信

進程之間不共享數(shù)據(jù)的。如果進程之間需要進行通信,則要用到Queue模塊或者Pipi模塊來實現(xiàn)。

Queue

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

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

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

具體用法如下:

from multiprocessing import Process, Queue
 def 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的本質(zhì)是進程之間的用管道數(shù)據(jù)傳遞,而不是數(shù)據(jù)共享,這和socket有點像。pipe() 返回兩個連接對象分別表示管道的兩端,每端都有send() 和recv()函數(shù)。

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

具體用法如下:

from multiprocessing import Process, Pipe
 def 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()

2.3 進程池

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

Pool 常用的方法如下:

具體用法見示例代碼:

from multiprocessing import Pool
def show(num):
 print('num : ' + str(num))
if __name__=="__main__":
 pool = Pool(processes = 3)
 for i in xrange(6):
  # 維持執(zhí)行的進程總數(shù)為processes,當一個進程執(zhí)行完畢后會添加新的進程進去
  pool.apply_async(show, args=(i, )) 
 print('======  apply_async  ======')
 pool.close()
 #調(diào)用join之前,先調(diào)用close函數(shù),否則會出錯。執(zhí)行完close后不會有新的進程加入到pool,join函數(shù)等待所有子進程結(jié)束
 pool.join()

3 Python 多線程

3.1 GIL

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

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

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

  • CPython:CPython 是用C語言實現(xiàn)的 Python 解釋器。 作為官方實現(xiàn),它是最廣泛使用的 Python 解釋器。
  • PyPy:PyPy 是用RPython實現(xiàn)的解釋器。RPython 是 Python 的子集, 具有靜態(tài)類型。這個解釋器的特點是即時編譯,支持多重后端(C, CLI, JVM)。PyPy 旨在提高性能,同時保持最大兼容性(參考 CPython 的實現(xiàn))。J
  • ython:Jython 是一個將 Python 代碼編譯成 Java 字節(jié)碼的實現(xiàn),運行在JVM (Java Virtual Machine) 上。另外,它可以像是用 Python 模塊一樣,導入 并使用任何Java類。IronPython:IronPython 是一個針對 .NET 框架的 Python 實現(xiàn)。它 可以用 Python 和 .NET framewor k的庫,也能將 Python 代碼暴露給 .NET 框架中的其他語言。

GIL 只在 CPython 中才有,而在 PyPy 和 Jython 中是沒有 GIL 的。
每次釋放 GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。這就導致打印線程執(zhí)行時長,會發(fā)現(xiàn)耗時更長的原因。

3.2 創(chuàng)建多線程

Python提供兩個模塊進行多線程的操作,分別是thread和threading,

前者是比較低級的模塊,用于更底層的操作,一般應用級別的開發(fā)不常用。

方法1:直接使用threading.Thread()

import threading
# 這個函數(shù)名可隨便定義
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()

方法2:繼承threading.Thread來自定義線程類,重寫run方法

import threading
class MyThread(threading.Thread):
 def __init__(self, n):
  super(MyThread, self).__init__()  # 重構(gòu)run函數(shù)必須要寫
  self.n = n
 def run(self):
  print("current task:", n)
if __name__ == "__main__":
 t1 = MyThread("thread 1")
 t2 = MyThread("thread 2")
 t1.start()
 t2.start()

3.3 線程合并

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

import threading
def count(n):
 while n > 0:
  n -= 1
if __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()

3.4 線程同步與互斥鎖

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

用法的基本步驟:

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

其中,鎖定方法acquire可以有一個超時時間的可選參數(shù)timeout。如果設定了timeout,則在超時后通過返回值可以判斷是否得到了鎖,從而可以進行一些其他的處理。

具體用法見示例代碼:

import threading
import time
num = 0
mutex = threading.Lock()
class MyThread(threading.Thread):
 def run(self):
  global num 
  time.sleep(1)
  if mutex.acquire(1):  
num = num + 1
msg = self.name + ': num value is ' + str(num)
print(msg)
mutex.release()
if __name__ == '__main__':
 for i in range(5):
  t = MyThread()
  t.start()

3.5 可重入鎖(遞歸鎖)

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

具體用法如下:

#創(chuàng)建 RLock
mutex = threading.RLock()
class MyThread(threading.Thread):
 def run(self):
  if mutex.acquire(1):
print("thread " + self.name + " get mutex")
time.sleep(1)
mutex.acquire()
mutex.release()
mutex.release()

3.6 守護線程

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

3.7 定時器

如果需要規(guī)定函數(shù)在多少秒后執(zhí)行某個操作,需要用到Timer類。具體用法如下:

from threading import Timer 
def show():
 print("Pyhton")
# 指定一秒鐘之后執(zhí)行 show 函數(shù)
t = Timer(1, hello)
t.start()  

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

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

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

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

以上就是分析詳解python多線程與多進程區(qū)別的詳細內(nèi)容,更多關(guān)于python多線程與多進程區(qū)別的資料請關(guān)注本站其它相關(guān)文章!

版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務器上建立鏡像,否則將依法追究法律責任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學習參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實時開通

自選配置、實時開通

免備案

全球線路精選!

全天候客戶服務

7x24全年不間斷在線

專屬顧問服務

1對1客戶咨詢顧問

在線
客服

在線客服:7*24小時在線

客服
熱線

400-630-3752
7*24小時客服服務熱線

關(guān)注
微信

關(guān)注官方微信
頂部