python 多進程、多線程、協程

一、python的多線程python

  多線程就是在同一時刻執行多個不一樣的程序,然而python中的多線程並不能真正的實現並行,這是因爲cpython解釋器中的GIL(全局解釋器鎖)搗的鬼,這把鎖保證了同一時刻只有一個線程被執行。linux

  多線程的特色:網絡

    線程比進程更輕量級,建立一個線程要比建立一個進程快10-100倍。多線程

    線程共享全局變量。併發

    因爲GIL的緣由,當一個線程遇到IO操做時,會切換到另外一個線程,因此線程適合IO密集型操做。app

    在多核cpu系統中,最大限度的利用多核,能夠開啓多個線程,開銷比進程小的多,可是這並不適合python。dom

  多線程互斥鎖:socket

    由於線程共享全局變量,因此須要互斥鎖去限制線程對全局變量的更改。async

    假設,當一個線程在執行到獲取全局變量的時候,這個後GIL切換到另外一個線程執行,這個時候新的線程爲全局變量+1後切換回以前的線程,以前的線程中的全局變量仍是+1前的值,因此須要互斥鎖。函數

  爲何有了GIL鎖還要互斥鎖呢?

    GIL鎖只是控制同一時刻下只有一個線程被執行,這並不能控制同一時刻只有一個線程去獲取並更改全局變量,因此須要使用互斥鎖。

  多線程的實現:

# 導入threading模塊
import threading # 定義全局變量
i=0 # 定義互斥鎖
mutex = threading.Lock() def a(): # 申明全局變量i
    global i for j in range(2000000): # 獲取互斥鎖
 mutex.acquire() i+=1
        # 釋放互斥鎖
 mutex.release() def b(): global i for j in range(2000000): mutex.acquire() i+=1 mutex.release() # 建立線程
t1 = threading.Thread(target=a) t2 = threading.Thread(target=b) # 開啓線程
t1.start() t2.start() # 等待全部線程結束
t1.join() t2.join() print(i)

二、python中的多進程

  python的多線程不能利用多核的優點,若是想要充分的利用多核cpu的資源,python中大部分狀況須要使用多進程。

  python多進程的特色:

    進程間不共享全局變量,進程修改的數據僅限於該進程內。

    進程建立和銷燬的開銷比較大。

    相對於線程,進程更適合與計算密集型操做。

    能充分利用多核的優點。  

  進程間通訊:

    既然進程間中不公共享全局變量,那麼多進程間怎麼進行通訊呢?能夠使用multiprocessing中的Queue模塊,固然也能夠使用socket、管道、共享內存等方式。

  多進程的實現:

# 導入multiprocessin模塊
import multiprocessing # 建立隊列
queue = multiprocessing.Queue() # 定義全局變量
a = 0 # 定義函數
def work1(num): # 獲取隊列中的數據,若是沒有數據,將堵塞
    a = queue.get() # 將隊列中的數據+2000000次num
    for i in range(2000000): a+=num # 將數據存放在隊列中
 queue.put(a) # 打印最終結果
    print("work1",a) # 定義函數
def work2(): # 申明全局變量a
    global a # 將a+2000000次1
    for i in range(2000000): a+=1
    # 打印最總結果
    print("work2",a) # 將a存放在隊列中
queue.put(a) # 建立進程
p1 = multiprocessing.Process(target=work1, args=(2,)) p2 = multiprocessing.Process(target=work2) # 啓動進程
p1.start() p2.start() # 等待進程結束
p1.join() p2.join() # 獲取隊列中的數據
a = queue.get() # 打印a
print(a)

  進程池的實現

  進程池能減小重複建立和銷燬進程的開銷問題

# 導入須要的模塊
import multiprocessing import time import random # 定義函數
def work(num): print("num=",num) time.sleep(random.randint(0,2)) # 建立進程池,設置進程的數量
pool = multiprocessing.Pool(3) for i in range(10): # 開啓進程
    pool.apply_async(work, args=(i,)) # 設置等待時間,等待全部進程結束
time.sleep(20)

三、python中的協程

  在linux中線程就是輕量級的進程,而咱們一般也把協程稱爲輕量級的線程。

  對比進程和協程:

    進程是內核調度,而協程是在用戶態調度,因此說進程的上下文在內核態保存恢復,而協程是在用戶態保存恢復的,因此協程的開銷比進程低。

    進程會被搶佔,而協程不會,也就是說協程若是不主動讓出cpu,那麼其餘的協程就沒有執行的機會。

    進程所須要的內存比協程大得多

  對比線程和協程:

    線程的上下文切換成本相對於協程來講比較高。

    線程的切換由操做系統來控制,而協程的切換由咱們本身控制。

  yield實現協程:

# 定義兩個函數
def work1(): while True: print("work1") # 當程序運行到yield就會暫停,等待下次的next調用,而後繼續執行
        yield
def work2(): while True: print("work2") yield w1 = work1() w2 = work2() while True: # 使用next函數啓動
 next(w1) next(w2)

   greenlet實現協程:

    greenlet安裝:   

sudo pip3 install greenlet

    code:

# 導入greenlet模塊
from greenlet import greenlet def work1(): for i in range(10): print("work1") # 打印事後跳轉至協程g2繼續執行
 g2.switch() def work2(): for i in range(10): print("work2") # 打印後跳轉至協程g1繼續執行
 g1.switch() # 建立協程g1
g1 = greenlet(work1) # 建立協程g2
g2 = greenlet(work2) # 跳轉至協程g1
g1.switch()

  gevent實現協程:

    gevent是基於greenlet的併發網絡庫,每當有一個協程堵塞的時,程序將自動調度。

    monkey-patching:

      通常稱爲猴子補丁,這個補丁能直接修改標準庫裏面大部分的阻塞式系統調用。可是若是在複雜的生產環境中使用了這些標準庫,可能就會由於打了補丁而出現奇怪的問題。

    gevent安裝:

sudo pip3 install gevent

    code:

# 導入所須要的模塊
import gevent import time from gevent import monkey # 猴子補丁,monkey.patch_all()方法將全部的標準庫都替換掉 # 使用猴子補丁褒貶不一,可是官網上仍是建議使用patch_all(),並且在程序的第一行就執行
monkey.patch_all() def f(n): for i in range(n): print(i) # 設置延時
        time.sleep(0.5) # 若是沒有導入monkey模塊的話,須要使用gevent.sleep()
        # gevent.sleep(0.5)

# ----------------寫法一-------------------- # 建立greenlet協程對象 # g1 = gevent.spawn(f,5) # g2 = gevent.spawn(f,5) # g3 = gevent.spawn(f,5) # 等待全部greenlet攜程結束後退出 # g1.join() # g2.join() # g3.join()

# ----------------寫法二--------------------
gevent.joinall([gevent.spawn(f,5), gevent.spawn(f,5), gevent.spawn(f,5)])
相關文章
相關標籤/搜索