日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

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

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

1. 進程

進程是正在運行的程序?qū)嵗?#xff0c;是內(nèi)核分配資源的最基本的單元。進程擁有自己獨立的堆和棧,獨立的地址空間,資源句柄。進程由 OS 調(diào)度,調(diào)度開銷較大,在并發(fā)的切換過程效率較低。

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

1.1 多進程示例

import os

from multiprocessing import Process

# 子進程執(zhí)行的代碼

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 指定要執(zhí)行的函數(shù),args 指定參數(shù)

print('Child process will start.')

p.start() #啟動 Process 實例

p.join() #等待子進程結(jié)束后,繼續(xù)往下執(zhí)行

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) # 創(chuàng)建對象池,并設置進程池大小,默認大小是 CPU 核數(shù)

for i in range(5):

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

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

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

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

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 等多種方式來交換數(shù)據(jù)。以 Queue 為例,在父進程中創(chuàng)建兩個子進程,一個往 Queue 里寫數(shù)據(jù),一個從 Queue 里讀數(shù)據(jù)。

import os, time, random

from multiprocessing import Process, Queue

def write(q): # 寫數(shù)據(jù)進程執(zhí)行的代碼

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): # 讀數(shù)據(jù)進程執(zhí)行的代碼

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

while True:

value = q.get(True)

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

if __name__ == '__main__':

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

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

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

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

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

pw.join() # 等待pw結(jié)束

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

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 調(diào)度和分派的基本單元。線程并不產(chǎn)生新的地址空間和資源描述符表,而是復用父進程的。線程只擁有程序計數(shù)器、一組寄存器和棧,同一進程的線程共享其他全部資源。

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

2.1 解釋器

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

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

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

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

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

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

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

2.2 全局鎖 GIL

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

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

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

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

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

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

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

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 模塊的區(qū)別: dummy 模塊是多線程,而 multiprocessing 是多進程, 調(diào)用方式相同。

from multiprocessing import Pool

from multiprocessing.dummy import Pool

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

3. 協(xié)程

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

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

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

3.1 Gevent

Gevent 是第三方庫,通過 Greenlet 實現(xiàn)協(xié)程,其基本實現(xiàn)原理是:

當一個 Greenlet 遇到 IO 操作時,比如訪問網(wǎng)絡,就自動切換到其他的 Greenlet,等到 IO 操作完成,再在適當?shù)臅r候切換回來繼續(xù)執(zhí)行。由于 IO 操作非常耗時,經(jīng)常使程序處于等待狀態(tài),有了 Gevent 為我們自動切換協(xié)程,就保證總有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 啟動協(xié)程,參數(shù)為函數(shù)名稱和參數(shù)名稱

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

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

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

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

3.2 Django

在 Django 中也會使用 Gevent 來增強并發(fā)能力,特別是對于 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 支持幾種并發(fā)模式,有 prefork,threading,協(xié)程(gevent,eventlet)。在 Celery 中使用并發(fā)模式,能顯著提高處理效率,特別是 IO 操作較多時。

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

-P 選項指定 pool,默認是 prefork,這里指定為 gevent, -c 設置并發(fā)數(shù)。

4. 最佳實踐

IO 密集型的任務(例如,網(wǎng)絡調(diào)用等)中使用線程和協(xié)程

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

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

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。