python並發編程之多進程、多線程、非同步和協程詳解_python

來源:互聯網
上載者:User

最近學習python並發,於是對多進程、多線程、非同步和協程做了個總結。
一、多線程

多線程就是允許一個進程記憶體在多個控制權,以便讓多個函數同時處於啟用狀態,從而讓多個函數的操作同時運行。即使是單CPU的電腦,也可以通過不停地在不同線程的指令間切換,從而造成多線程同時啟動並執行效果。

多線程相當於一個並發(concunrrency)系統。並發系統一般同時執行多個任務。如果多個任務可以共用資源,特別是同時寫入某個變數的時候,就需要解決同步的問題,比如多線程火車售票系統:兩個指令,一個指令檢查票是否賣完,另一個指令,多個視窗同時賣票,可能出現賣出不存在的票。

在並發情況下,指令執行的先後順序由核心決定。同一個線程內部,指令按照先後順序執行,但不同線程之間的指令很難說清除哪一個會先執行。因此要考慮多線程同步的問題。同步(synchronization)是指在一定的時間內只允許某一個線程訪問某個資源。

1、thread模組

2、threading模組
threading.Thread 建立一個線程。

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

#! /usr/bin/python#-* coding: utf-8 -*# __author__ ="tyomcat"import threadingimport timeimport osdef 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 = 100lock=threading.Lock()for k in range(10):  new_thread = threading.Thread(target=booth,args=(k,))  new_thread.start()

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

協程,與線程的搶佔式調度不同,它是協作式調度。協程也是單線程,但是它能讓原來要使用非同步+回調方式寫的非人類代碼,可以用看似同步的方式寫出來。

1、協程在python中可以由產生器(generator)來實現。

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

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

一旦函數將控制權交還給調用者,就意味著全部結束。而有時可以建立能產生一個序列的函數,來“儲存自己的工作”,這就是產生器(使用了yield關鍵字的函數)。

能夠“產生一個序列”是因為函數並沒有像通常意義那樣返回。return隱含的意思是函數正將執行代碼的控制權返回給函數被調用的地方。而"yield"的隱含意思是控制權的轉移是臨時和自願的,我們的函數將來還會收回控制權。

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

#! /usr/bin/python#-* coding: utf-8 -*# __author__ ="tyomcat"import timeimport 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異常。

2、Stackless Python

3、greenlet模組

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

4、eventlet模組

三、多進程
1、子進程(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 subprocesschild1 = 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中。

2、多進程(multiprocessing包)

(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 Poolimport os, timedef 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,Poolimport multiprocessing,time,randomdef 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 "所有資料都寫入並且讀完"

四、非同步

無論是線程還是進程,使用的都是同步進位,當發生阻塞時,效能會大幅度降低,無法充分利用CPU潛力,浪費硬體投資,更重要造成軟體模組的鐵板化,緊耦合,無法切割,不利於日後擴充和變化。

不管是進程還是線程,每次阻塞、切換都需要陷入系統調用(system call),先讓CPU跑作業系統的發送器,然後再由發送器決定該跑哪一個進程(線程)。多個線程之間在一些訪問互斥的代碼時還需要加上鎖,

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

非同步事件驅動模型中,把會導致阻塞的操作轉化為一個非同步作業,主線程負責發起這個非同步作業,並處理這個非同步作業的結果。由於所有阻塞的操作都轉化為非同步作業,理論上主線程的大部分時間都是在處理實際的計算任務,少了多線程的調度時間,所以這種模型的效能通常會比較好。

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.