欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

python threading和multiprocessing模塊基本用法實例分析

 更新時間:2019年07月25日 09:52:06   作者:zhaozhi406  
這篇文章主要介紹了python threading和multiprocessing模塊基本用法,結合實例形式詳細分析了Python中threading和multiprocessing模塊基本概念、功能、使用方法及相關操作注意事項,需要的朋友可以參考下

本文實例講述了python threading和multiprocessing模塊基本用法。分享給大家供大家參考,具體如下:

前言

這兩天為了做一個小項目,研究了一下python的并發(fā)編程,所謂并發(fā)無非多線程和多進程,最初找到的是threading模塊,因為印象中線程“輕量...”,“切換快...”,“可共享進程資源...”等等,但是沒想到這里水很深,進而找到了更好的替代品multiprocessing模塊。下面會講一些使用中的經(jīng)驗。

后面出現(xiàn)的代碼都在ubuntu10.04 + python2.6.5的環(huán)境下測試通過。

一、使用threading模塊創(chuàng)建線程

1、三種線程創(chuàng)建方式

(1)傳入一個函數(shù)

這種方式是最基本的,即調用threading中的Thread類的構造函數(shù),然后指定參數(shù)target=func,再使用返回的Thread的實例調用start()方法,即開始運行該線程,該線程將執(zhí)行函數(shù)func,當然,如果func需要參數(shù),可以在Thread的構造函數(shù)中傳入?yún)?shù)args=(...)。示例代碼如下:

#!/usr/bin/python
#-*-coding:utf-8-*-
import threading
#用于線程執(zhí)行的函數(shù)
def counter(n):
  cnt = 0;
  for i in xrange(n):
    for j in xrange(i):
      cnt += j;
  print cnt;
if __name__ == '__main__':
 #初始化一個線程對象,傳入函數(shù)counter,及其參數(shù)1000
  th = threading.Thread(target=counter, args=(1000,));
 #啟動線程
  th.start();
 #主線程阻塞等待子線程結束
  th.join();

這段代碼很直觀,counter函數(shù)是一個很無聊的雙重循環(huán),需要注意的是th.join()這句,這句的意思是主線程將自我阻塞,然后等待th表示的線程執(zhí)行完畢再結束,如果沒有這句,運行代碼會立即結束。join的意思比較晦澀,其實將這句理解成這樣會好理解些“while th.is_alive(): time.sleep(1)”。雖然意思相同,但是后面將看到,使用join也有陷阱。

(2)傳入一個可調用的對象

許多的python 對象都是我們所說的可調用的,即是任何能通過函數(shù)操作符“()”來調用的對象(見《python核心編程》第14章)。類的對象也是可以調用的,當被調用時會自動調用對象的內建方法__call__(),因此這種新建線程的方法就是給線程指定一個__call__方法被重載了的對象。示例代碼如下:

#!/usr/bin/python
#-*-coding:utf-8-*-
import threading
#可調用的類
class Callable(object):
  def __init__(self, func, args):
    self.func = func;
    self.args = args;
  def __call__(self):
    apply(self.func, self.args);
#用于線程執(zhí)行的函數(shù)
def counter(n):
  cnt = 0;
  for i in xrange(n):
    for j in xrange(i):
      cnt += j;
  print cnt;
if __name__ == '__main__':
 #初始化一個線程對象,傳入可調用的Callable對象,并用函數(shù)counter及其參數(shù)1000初始化這個對象
  th = threading.Thread(target=Callable(counter, (1000,)));
 #啟動線程
  th.start();
 #主線程阻塞等待子線程結束
  th.join();

這個例子關鍵的一句是apply(self.func, self.args); 這里使用初始化時傳入的函數(shù)對象及其參數(shù)來進行一次調用。

(3)繼承Thread類

這種方式通過繼承Thread類,并重載其run方法,來實現(xiàn)自定義的線程行為,示例代碼如下:

#!/usr/bin/python
#-*-coding:utf-8-*-
import threading, time, random
def counter():
  cnt = 0;
  for i in xrange(10000):
    for j in xrange(i):
      cnt += j;
class SubThread(threading.Thread):
  def __init__(self, name):
    threading.Thread.__init__(self, name=name);
  def run(self):
    i = 0;
    while i < 4:
      print self.name,'counting...\n';
      counter();
      print self.name,'finish\n';
      i += 1;
if __name__ == '__main__':
  th = SubThread('thread-1');
  th.start();
  th.join();
  print 'all done';

這個例子定義了一個SubThread類,它繼承了Thread類,并重載了run方法,在方法中調用counter4次并打印一些信息,可以看到這種方式比較直觀。在構造函數(shù)中要記得先調用父類的構造函數(shù)進行初始化。

2、python多線程的限制

python多線程有個討厭的限制,全局解釋器鎖(global interpreter lock),這個鎖的意思是任一時間只能有一個線程使用解釋器,跟單cpu跑多個程序一個意思,大家都是輪著用的,這叫“并發(fā)”,不是“并行”。手冊上的解釋是為了保證對象模型的正確性!這個鎖造成的困擾是如果有一個計算密集型的線程占著cpu,其他的線程都得等著....,試想你的多個線程中有這么一個線程,得多悲劇,多線程生生被搞成串行;當然這個模塊也不是毫無用處,手冊上又說了:當用于IO密集型任務時,IO期間線程會釋放解釋器,這樣別的線程就有機會使用解釋器了!所以是否使用這個模塊需要考慮面對的任務類型。

二、使用multiprocessing創(chuàng)建進程

1、三種創(chuàng)建方式

進程的創(chuàng)建方式跟線程完全一致,只不過要將threading.Thread換成multiprocessing.Process。multiprocessing模塊盡力保持了與threading模塊在方法名上的一致性,示例代碼可參考上面線程部分的。這里只給出第一種使用函數(shù)的方式:

#!/usr/bin/python
#-*-coding:utf-8-*-
import multiprocessing, time
def run():
  i = 0;
  while i<10000:
    print 'running';
    time.sleep(2);
    i += 1;
if __name__ == '__main__':
  p = multiprocessing.Process(target=run);
  p.start();
  #p.join();
  print p.pid;
  print 'master gone';

2、創(chuàng)建進程池

該模塊還允許一次創(chuàng)建一組進程,然后再給他們分配任務。詳細內容可參考手冊,這部分研究不多,不敢亂寫。

pool = multiprocessing.Pool(processes=4)
pool.apply_async(func, args...)

3、使用進程的好處

完全并行,無GIL的限制,可充分利用多cpu多核的環(huán)境;可以接受linux信號,后面將看到,這個功能非常好用。

三、實例研究

該實例假想的任務是:一個主進程會啟動多個子進程分別處理不同的任務,各個子進程可能又有自己的線程用于不同的IO處理(前面說過,線程在IO方面還是不錯的),要實現(xiàn)的功能是,對這些子進程發(fā)送信號,能被正確的處理,例如發(fā)生SIGTERM,子進程能通知其線程收工,然后“優(yōu)雅”的退出。現(xiàn)在要解決的問題有:(1)在子類化的Process對象中如何捕捉信號;(2)如何“優(yōu)雅的退出”。下面分別說明。

1、子類化Process并捕捉信號

如果是使用第一種進程創(chuàng)建方式(傳入函數(shù)),那么捕捉信號很容易,假設給進程運行的函數(shù)叫func,代碼示例如下:

#!/usr/bin/python
#-*-coding:utf-8-*-
import multiprocessing, signal,time
def handler(signum, frame):
  print 'signal', signum;
def run():
  signal.signal(signal.SIGTERM, handler);
  signal.signal(signal.SIGINT, handler);
  i = 0;
  while i<10000:
    print 'running';
    time.sleep(2);
    i += 1;
if __name__ == '__main__':
  p = multiprocessing.Process(target=run);
  p.start();
  #p.join();
  print p.pid;
  print 'master gone';

這段代碼是在第一種創(chuàng)建方式的基礎上修改而來的,增加了兩行signal.signal(...)調用,這是說這個函數(shù)要捕捉SIGTERM和SIGINT兩個信號,另外增加了一個handler函數(shù),該函數(shù)用于捕捉到信號時進行相應的處理,我們這里只是簡單的打印出信號值。

注意p.join()被注釋掉了,這里跟線程的情況有點區(qū)別,新的進程啟動后就開始運行了,主進程也不用等待它運行完,可以該干嘛干嘛去。這段代碼運行后會打印出子進程的進程id,根據(jù)這個id,在另一個終端輸入kill -TERM id,會發(fā)現(xiàn)剛才的終端打印出了"signal 15"。

但是使用傳入函數(shù)的方式有一點不好的是封裝性太差,如果功能稍微復雜點,將會有很多的全局變量暴露在外,最好還是將功能封裝成類,那么使用類又怎么注冊信號相應函數(shù)呢?上面的例子貌似只能使用一個全局的函數(shù),手冊也沒有給出在類中處理信號的例子,其實解決方法大同小異,也很容易,這個帖子http://stackoverflow.com/questions/6204443/python-signal-reading-return-from-signal-handler-function給了我靈感:

class Master(multiprocessing.Process):
  def __init__(self):
    super(Master,self).__init__();
    signal.signal(signal.SIGTERM, self.handler);   #注冊信號處理函數(shù)
    self.live = 1;
  #信號處理函數(shù)
  def handler(self, signum, frame):
    print 'signal:',signum;
    self.live = 0;
  def run(self):
    print 'PID:',self.pid;
    while self.live:
      print 'living...'
      time.sleep(2);

方法很直觀,首先在構造函數(shù)中注冊信號處理函數(shù),然后定義了一個方法handler作為處理函數(shù)。這個進程類會每隔2秒打印一個“l(fā)iving...”,當接收到SIGTERM后,改變self.live的值,run方法的循環(huán)檢測到這個值為0后就結束了,進程也結束了。

2、讓進程優(yōu)雅的退出

下面放出這次的假想任務的全部代碼,我在主進程中啟動了一個子進程(通過子類化Process類),然后子進程啟動后又產(chǎn)生兩個子線程,用來模擬“生產(chǎn)者-消費者”模型,兩個線程通過一個隊列進行交流,為了互斥訪問這個隊列,自然要加一把鎖(condition對象跟Lock對象差不多,不過多了等待和通知的功能);生產(chǎn)者每次產(chǎn)生一個隨機數(shù)并扔進隊列,然后休息一個隨機時間,消費者每次從隊列取一個數(shù);而子進程中的主線程要負責接收信號,以便讓整個過程優(yōu)雅的結束。代碼如下:

#!/usr/bin/python
#-*-coding:utf-8-*-
import time, multiprocessing, signal, threading, random, time, Queue
class Master(multiprocessing.Process):
  def __init__(self):
    super(Master,self).__init__();
    signal.signal(signal.SIGTERM, self.handler);
 #這個變量要傳入線程用于控制線程運行,為什么用dict?充分利用線程間共享資源的特點
 #因為可變對象按引用傳遞,標量是傳值的,不信寫成self.live = true試試
    self.live = {'stat':True};
  def handler(self, signum, frame):
    print 'signal:',signum;
    self.live['stat'] = 0;                  #置這個變量為0,通知子線程可以“收工”了
  def run(self):
    print 'PID:',self.pid;
    cond = threading.Condition(threading.Lock());      #創(chuàng)建一個condition對象,用于子線程交互
    q = Queue.Queue();                    #一個隊列
    sender = Sender(cond, self.live, q);           #傳入共享資源
    geter = Geter(cond, self.live, q);
    sender.start();                     #啟動線程
    geter.start();
    signal.pause();                     #主線程睡眠并等待信號
    while threading.activeCount()-1:             #主線程收到信號并被喚醒后,檢查還有多少線程活著(除掉自己)
      time.sleep(2);                    #再睡眠等待,確保子線程都安全的結束
      print 'checking live', threading.activeCount();
    print 'mater gone';
class Sender(threading.Thread):
  def __init__(self, cond, live, queue):
    super(Sender, self).__init__(name='sender');
    self.cond = cond;
    self.queue = queue;
    self.live = live
  def run(self):
    cond = self.cond;
    while self.live['stat']:                 #檢查這個進程內的“全局”變量,為真就繼續(xù)運行
      cond.acquire();                   #獲得鎖,以便控制隊列
      i = random.randint(0,100);
      self.queue.put(i,False);
      if not self.queue.full():
        print 'sender add:',i;
      cond.notify();                    #喚醒等待鎖的其他線程
      cond.release();                   #釋放鎖
      time.sleep(random.randint(1,3));
    print 'sender done'
class Geter(threading.Thread):
  def __init__(self, cond, live, queue):
    super(Geter, self).__init__(name='geter');
    self.cond = cond;
    self.queue = queue;
    self.live = live
  def run(self):
    cond = self.cond;
    while self.live['stat']:
      cond.acquire();
      if not self.queue.empty():
        i = self.queue.get();
        print 'geter get:',i;
      cond.wait(3);
      cond.release();
      time.sleep(random.randint(1,3));
    print 'geter done'
if __name__ == '__main__':
  master = Master();
  master.start();                       #啟動子進程

需要注意的地方是,在Master的run方法中sender.start()geter.start()之后,按常理應該接著調用sender.join()geter.join(),讓主線程等待子線程結束,前面說的join的陷阱就在這里,join將主線程阻塞(blocking)住了,主線程無法再捕捉信號,剛開始研究這塊時還以為信號處理函數(shù)寫錯了。網(wǎng)上討論比較少,這里說的比較清楚http://stackoverflow.com/questions/631441/interruptible-thread-join-in-python,http://www.gossamer-threads.com/lists/python/python/541403

參考:

《python核心編程》
《python manual》

更多關于Python相關內容感興趣的讀者可查看本站專題:《Python進程與線程操作技巧總結》、《Python數(shù)據(jù)結構與算法教程》、《Python函數(shù)使用技巧總結》、《Python字符串操作技巧匯總》、《Python入門與進階經(jīng)典教程》、《Python+MySQL數(shù)據(jù)庫程序設計入門教程》及《Python常見數(shù)據(jù)庫操作技巧匯總

希望本文所述對大家Python程序設計有所幫助。

相關文章

  • Python3.2中的字符串函數(shù)學習總結

    Python3.2中的字符串函數(shù)學習總結

    這篇文章主要介紹了Python3.2中的字符串函數(shù)學習總結,本文講解了格式化類方法、查找 & 替換類方法、拆分 & 組合類方法等內容,需要的朋友可以參考下
    2015-04-04
  • 使用Python操作PDF文件

    使用Python操作PDF文件

    這篇文章介紹了Python操作PDF文件的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • Python數(shù)據(jù)結構之優(yōu)先級隊列queue用法詳解

    Python數(shù)據(jù)結構之優(yōu)先級隊列queue用法詳解

    queue庫提供了一個適用于多線程編程的先進先出(FIFO)數(shù)據(jù)結構,可以用來在生產(chǎn)者與消費者線程之間安全地傳遞消息或其他數(shù)據(jù),它會為調用者處理鎖定,使多個線程可以安全而更容易地處理同一個Queue實例.Queue的大小可能受限,以限制內存使用或處理,需要的朋友可以參考下
    2021-05-05
  • Python提取PDF中的圖片的實現(xiàn)示例

    Python提取PDF中的圖片的實現(xiàn)示例

    本文主要介紹了Python提取PDF中的圖片的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • python基礎之Socket套接字詳解

    python基礎之Socket套接字詳解

    這篇文章主要介紹了python基礎之Socket套接字詳解,文中有非常詳細的代碼示例,對正在學習python基礎的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-04-04
  • tensorflow: 查看 tensor詳細數(shù)值方法

    tensorflow: 查看 tensor詳細數(shù)值方法

    今天小編就為大家分享一篇tensorflow: 查看 tensor詳細數(shù)值方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06
  • python 自動刷新網(wǎng)頁的兩種方法

    python 自動刷新網(wǎng)頁的兩種方法

    這篇文章主要介紹了python 自動刷新網(wǎng)頁的兩種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • Python登錄QQ郵箱發(fā)送郵件的實現(xiàn)示例

    Python登錄QQ郵箱發(fā)送郵件的實現(xiàn)示例

    本文主要介紹了Python登錄QQ郵箱發(fā)送郵件的實現(xiàn)示例,主要就是三步,登錄郵件、寫郵件內容、發(fā)送,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧<BR>
    2023-08-08
  • Python 使用pandas實現(xiàn)查詢和統(tǒng)計示例詳解

    Python 使用pandas實現(xiàn)查詢和統(tǒng)計示例詳解

    這篇文章主要為大家介紹了Python 使用pandas實現(xiàn)查詢和統(tǒng)計示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • python+django+mysql開發(fā)實戰(zhàn)(附demo)

    python+django+mysql開發(fā)實戰(zhàn)(附demo)

    本文主要介紹了python+django+mysql開發(fā)實戰(zhàn)(附demo),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評論