python併發編程之多進程、多線程、異步和協程

1、多線程

  多線程就是容許一個進程內存在多個控制權,以便讓多個函數同時處於激活狀態,從而讓多個函數的操做同時運行。即便是單CPU的計算機,也能夠經過不停地在不一樣線程的指令間切換,從而形成多線程同時運行的效果。html

  多線程至關於一個併發(concunrrency)系統。併發系統通常同時執行多個任務。若是多個任務能夠共享資源,特別是同時寫入某個變量的時候,就須要解決同步的問題,好比多線程火車售票系統:兩個指令,一個指令檢查票是否賣完,另外一個指令,多個窗口同時賣票,可能出現賣出不存在的票。python

  在併發狀況下,指令執行的前後順序由內核決定。同一個線程內部,指令按照前後順序執行,但不一樣線程之間的指令很難說清除哪個會先執行。所以要考慮多線程同步的問題。同步(synchronization)是指在必定的時間內只容許某一個線程訪問某個資源。 nginx

詳情請看:Linux多線程與同步編程

一、thread模塊

二、threading模塊

threading.Thread 建立一個線程。服務器

給判斷是否有餘票和賣票,加上互斥鎖,這樣就不會形成一個線程剛判斷沒有餘票,而另一個線程就執行賣票操做。多線程

複製代碼
#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import threading
import time
import os

def booth(tid):
    global i
    global lock
    while True:
        lock.acquire()
        if i!=0:
            i=i-1
            print "窗口:",tid,",剩餘票數:",i
            time.sleep(1)
        else:
            print "Thread_id",tid,"No more tickets"
            os._exit(0)
        lock.release()
        time.sleep(1)

i = 100
lock=threading.Lock()

for k in range(10):

    new_thread = threading.Thread(target=booth,args=(k,))
    new_thread.start()
複製代碼

2、協程(又稱微線程,纖程)

  協程,與線程的搶佔式調度不一樣,它是協做式調度。協程也是單線程,可是它能讓原來要使用異步+回調方式寫的非人類代碼,能夠用看似同步的方式寫出來。併發

一、協程在python中能夠由生成器(generator)來實現。app

  首先要對生成器和yield有一個紮實的理解.less

  調用一個普通的python函數,通常是從函數的第一行代碼開始執行,結束於return語句、異常或者函數執行(也能夠認爲是隱式地返回了None)。dom

一旦函數將控制權交還給調用者,就意味着所有結束。而有時能夠建立能產生一個序列的函數,來「保存本身的工做」,這就是生成器(使用了yield關鍵字的函數)。

  可以「產生一個序列」是由於函數並無像一般意義那樣返回。return隱含的意思是函數正將執行代碼的控制權返回給函數被調用的地方。而"yield"的隱含意思是控制權的轉移是臨時和自願的,咱們的函數未來還會收回控制權。

 詳情請看:解釋'yield'和'Generators(生成器)'

看一下生產者/消費者的例子:

複製代碼
#! /usr/bin/python
#-* coding: utf-8 -*
# __author__ ="tyomcat"
import time
import sys
# 生產者
def produce(l):
    i=0
    while 1:
        if i < 10:
            l.append(i)
            yield i
            i=i+1
            time.sleep(1)
        else:
            return     
# 消費者
def consume(l):
    p = produce(l)
    while 1:
        try:
            p.next()
            while len(l) > 0:
                print l.pop()
        except StopIteration:
            sys.exit(0)
if __name__ == "__main__":
    l = []
    consume(l)
複製代碼

當程序執行到produce的yield i時,返回了一個generator並暫停執行,當咱們在custom中調用p.next(),程序又返回到produce的yield i 繼續執行,這樣 l 中又append了元素,而後咱們print l.pop(),直到p.next()引起了StopIteration異常。

二、Stackless Python

三、greenlet模塊

  基於greenlet的實現則性能僅次於Stackless Python,大體比Stackless Python慢一倍,比其餘方案快接近一個數量級。其實greenlet不是一種真正的併發機制,而是在同一線程內,在不一樣函數的執行代碼塊之間切換,實施「你運行一會、我運行一會」,而且在進行切換時必須指定什麼時候切換以及切換到哪。

四、eventlet模塊

詳情請看:Python幾種併發實現方案的性能比較

3、多進程

一、子進程(subprocess包)

  在python中,經過subprocess包,fork一個子進程,並運行外部程序。

  調用系統的命令的時候,最早考慮的os模塊。用os.system()和os.popen()來進行操做。可是這兩個命令過於簡單,不能完成一些複雜的操做,如給運行的命令提供輸入或者讀取命令的輸出,判斷該命令的運行狀態,管理多個命令的並行等等。這時subprocess中的Popen命令就能有效的完成咱們須要的操做

>>>import subprocess
>>>command_line=raw_input()
ping -c 10 www.baidu.com
>>>args=shlex.split(command_line)
>>>p=subprocess.Popen(args)

  利用subprocess.PIPE將多個子進程的輸入和輸出鏈接在一塊兒,構成管道(pipe):

import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)

communicate() 方法從stdout和stderr中讀出數據,並輸入到stdin中。

二、多進程(multiprocessing包)

詳情請看:Python多進程編程

  (1)、multiprocessing包是Python中的多進程管理包。與threading.Thread相似,它能夠利用multiprocessing.Process對象來建立一個進程。

  進程池 (Process Pool)能夠建立多個進程。

  apply_async(func,args)  從進程池中取出一個進程執行func,args爲func的參數。它將返回一個AsyncResult的對象,你能夠對該對象調用get()方法以得到結果。

  close()  進程池再也不建立新的進程

  join()   wait進程池中的所有進程。必須對Pool先調用close()方法才能join。

複製代碼
#! /usr/bin/env python
# -*- coding:utf-8   -*-
# __author__ == "tyomcat"
# "個人電腦有4個cpu"

from multiprocessing import Pool
import os, time

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()
    for i in range(4):
        p.apply_async(long_time_task, args=(i,))
    print 'Waiting for all subprocesses done...'
    p.close()
    p.join()
    print 'All subprocesses done.'

複製代碼
 

(2)、多進程共享資源


經過共享內存和Manager對象:用一個進程做爲服務器,創建Manager來真正存放資源。
其它的進程能夠經過參數傳遞或者根據地址來訪問Manager,創建鏈接後,操做服務器上的資源。
複製代碼
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ == "tyomcat"

from multiprocessing import Queue,Pool
import multiprocessing,time,random

def write(q):

for value in ['A','B','C','D']:
print "Put %s to Queue!" % value
q.put(value)
time.sleep(random.random())


def read(q,lock):
while True:
lock.acquire()
if not q.empty():
value=q.get(True)
print "Get %s from Queue" % value
time.sleep(random.random())
else:
break
lock.release()

if __name__ == "__main__":
manager=multiprocessing.Manager()
q=manager.Queue()
p=Pool()
lock=manager.Lock()
pw=p.apply_async(write,args=(q,))
pr=p.apply_async(read,args=(q,lock))
p.close()
p.join()
print
print "全部數據都寫入而且讀完"
複製代碼

4、異步

  不管是線程仍是進程,使用的都是同步進制,當發生阻塞時,性能會大幅度下降,沒法充分利用CPU潛力,浪費硬件投資,更重要形成軟件模塊的鐵板化,緊耦合,沒法切割,不利於往後擴展和變化。

  不論是進程仍是線程,每次阻塞、切換都須要陷入系統調用(system call),先讓CPU跑操做系統的調度程序,而後再由調度程序決定該跑哪個進程(線程)。多個線程之間在一些訪問互斥的代碼時還須要加上鎖,

  現下流行的異步server都是基於事件驅動的(如nginx)。

  異步事件驅動模型中,把會致使阻塞的操做轉化爲一個異步操做,主線程負責發起這個異步操做,並處理這個異步操做的結果。因爲全部阻塞的操做都轉化爲異步操做,理論上主線程的大部分時間都是在處理實際的計算任務,少了多線程的調度時間,因此這種模型的性能一般會比較好。

相關文章
相關標籤/搜索