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

歡迎訪問 生活随笔!

生活随笔

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

python

python 协程、进程、线程_Python 中的进程、线程、协程

發布時間:2024/4/11 python 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 协程、进程、线程_Python 中的进程、线程、协程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 進程

進程是正在運行的程序實例,是內核分配資源的最基本的單元。進程擁有自己獨立的堆和棧,獨立的地址空間,資源句柄。進程由 OS 調度,調度開銷較大,在并發的切換過程效率較低。

Python 提供了一個跨平臺的多進程模塊 multiprocessing,模塊中使用 Process 類來代表一個進程對象。

1.1 多進程示例

import os

from multiprocessing import Process

# 子進程執行的代碼

def run_proc(name):

print('Run child process %s (%s)...' % (name, os.getpid()))

if __name__=='__main__':

print('Parent process %s.' % os.getpid())

p = Process(target=run_proc, args=('test',)) # target 指定要執行的函數,args 指定參數

print('Child process will start.')

p.start() #啟動 Process 實例

p.join() #等待子進程結束后,繼續往下執行

print('Child process end.')

Parent process 274.

Child process will start.

Run child process test (298)...

Child process end.

1.2 進程池示例

import os, time

from multiprocessing import Pool

def long_time_task(name):

print('Run task %s (%s)...' % (name, os.getpid()))

start = time.time()

time.sleep(3)

end = time.time()

print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':

print('Parent process %s.' % os.getpid())

p = Pool(2) # 創建對象池,并設置進程池大小,默認大小是 CPU 核數

for i in range(5):

p.apply_async(long_time_task, args=(i,)) # 設置每個進程要執行的函數和參數,異步執行

print('Waiting for all subprocesses done...')

p.close() # 關閉進程池,不允許繼續添加新的 Process

p.join() # 等待全部子進程執行完畢

print('All subprocesses done.')

Parent process 274.

Run task 1 (431)...

Run task 0 (430)...

Waiting for all subprocesses done...

Task 1 runs 3.00 seconds.

Run task 2 (431)...

Task 0 runs 3.00 seconds.

Run task 3 (430)...

Task 2 runs 3.00 seconds.

Task 3 runs 3.00 seconds.

Run task 4 (431)...

Task 4 runs 3.00 seconds.

All subprocesses done.

1.3 進程間通信

multiprocessing 模塊封裝了底層的通信機制,提供了 Queue、Pipes 等多種方式來交換數據。以 Queue 為例,在父進程中創建兩個子進程,一個往 Queue 里寫數據,一個從 Queue 里讀數據。

import os, time, random

from multiprocessing import Process, Queue

def write(q): # 寫數據進程執行的代碼

print("Process to write: %s" % os.getpid())

for value in ['A', 'B', 'C']:

print("Put %s to queue..." % value)

q.put(value)

time.sleep(random.random())

def read(q): # 讀數據進程執行的代碼

print("Process to read: %s" % os.getpid())

while True:

value = q.get(True)

print("Get %s from queue." % value)

if __name__ == '__main__':

q = Queue() # 父進程創建Queue,并傳給各個子進程

pw = Process(target=write, args=(q,))

pr = Process(target=read, args=(q,))

pw.start() # 啟動子進程pw,寫入

pr.start() # 啟動子進程pr,讀取

pw.join() # 等待pw結束

pr.terminate() # pr進程里的死循環,無法等待結束,只能強制終止

Process to write: 211

Put A to queue...

Process to read: 212

Get A from queue.

Put B to queue...

Get B from queue.

Put C to queue...

Get C from queue.

2. 線程

線程是一種輕量進程,是 CPU 調度和分派的基本單元。線程并不產生新的地址空間和資源描述符表,而是復用父進程的。線程只擁有程序計數器、一組寄存器和棧,同一進程的線程共享其他全部資源。

線程由 OS 調度,相較于進程,線程調度的成本非常小。線程間通信主要通過共享內存,上下文切換很快,資源開銷較少,但相比進程不夠穩定容易丟失數據。

2.1 解釋器

在談 Python 的線程之前,先了解下 Python 的幾個解釋器版本:

CPython ,Python 的官方版本,使用 C 語言實現,使用最為廣泛,大部分人使用的都是這個版本。

Jython,Python 的 Java 實現,相比于 CPython,與 Java 語言之間的互操作性要遠遠高于 CPython 和 C 語言之間的互操作性。

Python for .NET,CPython 實現的 .NET 托管版本,與 .NET 庫和程序代碼有很好的互操作性。

IronPython,不同于 Python for .NET,它是 Python 的 C# 實現,并且它將 Python 代碼編譯成 C# 中間代碼(與 Jython 類似),與.NET語言的互操作性也非常好。

PyPy,Python 的 Python 實現版本。PyPy 運行在 CPython(或者其它實現)之上,用戶程序運行在 PyPy 之上。目標是成為 Python 語言自身的試驗場,可以很容易地修改 PyPy 解釋器的實現(因為是使用Python寫的)。

Stackless,Stackless Python 是 CPython 的一個增強版本,它使程序員從基于線程的編程方式中獲得好處,并避免傳統線程所帶來的性能與復雜度問題。

2.2 全局鎖 GIL

GIL 是 CPython 中特有的全局解釋器鎖(其它 Python 版本解釋器,有自己的線程調度機制,沒有GIL機制)。本質上,GIL 就是 Python 進程中的一把超大鎖,在解釋器進程中是全局有效。GIL 主要鎖定的是 CPU 執行資源,實現線程獨占。

在 CPython 解釋器中,當一個線程需要使用 CPU 資源時,首先得獲取 GIL,直到遇到 I/O 操作時,才會釋放 GIL。

如果是 I/O 密集型線程,多線程能比單線程顯著提高性能;如果是 CPU 密集型線程,多線程并不能提高性能,因為等待 GIL,多線程也只能依次按順序執行。

在單核 CPU 中,同一時刻僅有一個線程占用 CPU,GIL 不會對 CPU 的使用率產生影響。但是在多核 CPU 中,由于 GIL 的存在,同一時刻,不同核的線程會競爭 GIL。獲取到 GIL 的線程能夠占用 CPU,而其他線程將處于閑置狀態,即使這些線程有空閑的 CPU 資源。

在 Python 3 中 GIL 也沒有去掉,因為有大量的第三方庫依賴 GIL。去掉 GIL 之后,需要引入復雜的鎖機制保護眾多全局狀態。

2.3 多線程示例

Python 的標準庫提供了兩個模塊:thread 和 threading,thread 是低級模塊,threading 是高級模塊,對 thread 進行了封裝。

import time, os, threading

start = time.time()

def doubler(number):

print(threading.currentThread().getName())

print('Parent process %s.' % os.getpid())

print(number * 2)

time.sleep(2)# 或者 IO 請求

print('thread run %0.2f s end'% (time.time() - start))

if __name__ == '__main__':

for i in range(3):

my_thread = threading.Thread(target=doubler, args=(i,))

my_thread.start()

#my_thread.join()

Thread-98

Parent process 426.

0

Thread-99

Parent process 426.

2

Thread-100

Parent process 426.

4

thread run 2.00 s end

thread run 2.01 s end

thread run 2.01 s end

由于線程中執行了 sleep ,釋放了 CPU 資源,其他線程得以執行。如果新增注釋部分的代碼?my_thread.join(), 那么線程將串行執行:

Thread-101

Parent process 426.

0

thread run 2.01 s end

Thread-102

Parent process 426.

2

thread run 4.01 s end

Thread-103

Parent process 426.

4

thread run 6.02 s end

2.4 multiprocessing.dummy

multiprocessing.dummy 模塊與 multiprocessing 模塊的區別: dummy 模塊是多線程,而 multiprocessing 是多進程, 調用方式相同。

from multiprocessing import Pool

from multiprocessing.dummy import Pool

與 multiprocessing 類似,dummy 模塊提供了多線程池,可以很方便將代碼在多線程和多進程之間切換。dummy 模塊在大量的開源項目中有所應用,十分推薦使用。

3. 協程

協程是一種輕量級的線程。協程擁有獨立的寄存器上下文和棧,同一個線程,共享堆。協程不由 OS 調度,OS 對于協程的一無所知,完全由程序員編碼進行控制。

具體點就是,執行函數 A 時,可以隨時中斷,去執行函數 B,接著中斷 B ,繼續執行函數A。而這些切換完全由程序吱聲控制。協程調度實際上是在同一線程中,進行程序函數的切換,沒有切換線程帶來的開銷。

協程比較適合處理 IO 密集型的任務。

3.1 Gevent

Gevent 是第三方庫,通過 Greenlet 實現協程,其基本實現原理是:

當一個 Greenlet 遇到 IO 操作時,比如訪問網絡,就自動切換到其他的 Greenlet,等到 IO 操作完成,再在適當的時候切換回來繼續執行。由于 IO 操作非常耗時,經常使程序處于等待狀態,有了 Gevent 為我們自動切換協程,就保證總有Greenlet 在運行,而不是等待 IO。

import gevent

import time, os, threading

from gevent import monkey;

monkey.patch_all() # 將默認阻塞的模塊替換成非阻塞

start = time.time()

def doubler(number):

print('Parent process %s.' % os.getpid())

print(number * 2)

time.sleep(2)

print('run %0.2f s end'% (time.time() - start))

if __name__ == '__main__':

tasks=[gevent.spawn(doubler,i) for i in range(3)] # gevent.spawn 啟動協程,參數為函數名稱和參數名稱

gevent.joinall(tasks) # gevent.joinall 等待執行完畢

Parent process 871.

0

Parent process 871.

2

Parent process 871.

4

run 2.00 s end

run 2.00 s end

run 2.00 s end

從結果來看,Python 中多線程和多協程的效果類似,在當前執行阻塞時,切換執行流程。不同的是,多線程切換的是線程,而協程切換的是正在執行的函數上下文。

使用 Gevent,可以獲得極高的并發性能,但 Gevent 只能在 Unix/Linux下運行,在 Windows 下不保證正常安裝和運行。

3.2 Django

在 Django 中也會使用 Gevent 來增強并發能力,特別是對于 IO 密集型的請求較多時:

# 使用 uwsgi 部署

$ uwsgi --gevent 100 --gevent-monkey-patch --http :8000 -M --processes 4 --wsgi-file wsgi.py

# 使用 gunicorn 部署

$ gunicorn --worker-class=gevent wsgi:application -b 0.0.0.0:8000

3.3 Celery

Celery 支持幾種并發模式,有 prefork,threading,協程(gevent,eventlet)。在 Celery 中使用并發模式,能顯著提高處理效率,特別是 IO 操作較多時。

$ celery worker -A celery_worker.celery -P gevent -c 10 -l INFO

-P 選項指定 pool,默認是 prefork,這里指定為 gevent, -c 設置并發數。

4. 最佳實踐

IO 密集型的任務(例如,網絡調用等)中使用線程和協程

CPU 密集的任務,需要使用多個進程,繞開 GIL 限制,充分利用多核 CPU ,提高效率

為了充分利用 CPU ,可以結合多進程+協程進行部署,多個進程,每個進程中多個協程。

超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生

總結

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

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