python多線程入門之旅一

所有代碼來自python核心編程

參考python核心編程一書,學(xué)習(xí)多線程工作模式,多線程實現(xiàn)主要模塊thread,threading,Queue等。

首先實現(xiàn)單線程一段代碼:

from time import sleep,ctime

def loop0():
    print 'start loop 0 at:', ctime()
    sleep(4)
    print 'loop 0 done at:', ctime()

def loop1():
    print 'start loop 1 at:', ctime()
    sleep(2)
    print 'loop 1 done at:', ctime()

def main():
    print 'starting at:', ctime()
    loop0()
    loop1()
    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

運行結(jié)果如下:能看到兩個函數(shù)按順序在6秒內(nèi)完成

starting at: Thu Jun 08 16:42:00 2017
start loop 0 at: Thu Jun 08 16:42:00 2017
loop 0 done at: Thu Jun 08 16:42:04 2017
start loop 1 at: Thu Jun 08 16:42:04 2017
loop 1 done at: Thu Jun 08 16:42:06 2017
all done at: Thu Jun 08 16:42:06 2017

我們將首先利用thread修改代碼,來看看效果

import thread
from time import sleep,ctime

def loop0():
    print 'start loop 0 at:', ctime()
    sleep(4)
    print 'loop 0 done at:', ctime()

def loop1():
    print 'start loop 1 at:', ctime()
    sleep(2)
    print 'loop 1 done at:', ctime()

def main():
    print 'starting at:', ctime()
    a=thread.start_new_thread(loop0,())
    b=thread.start_new_thread(loop1,())
    sleep(6)
    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

運行結(jié)果如下:我們能看到loop0和loop1是并發(fā)執(zhí)行的

starting at: Thu Jun 08 16:44:44 2017
start loop 1 at: Thu Jun 08 16:44:44 2017
start loop 0 at: Thu Jun 08 16:44:44 2017
loop 1 done at: Thu Jun 08 16:44:46 2017
loop 0 done at: Thu Jun 08 16:44:48 2017
all done at: Thu Jun 08 16:44:50 2017

我們添加了sleep(6)來實現(xiàn)子線程與主線程的同步,我們可以嘗試把sleep(6)改為sleep(2)來看看效果:

starting at: Thu Jun 08 16:47:52 2017
start loop 0 at: Thu Jun 08 16:47:52 2017
start loop 1 at: Thu Jun 08 16:47:52 2017
all done at: Thu Jun 08 16:47:54 2017
Unhandled exception in thread started by 
sys.excepthook is missing
lost sys.stderr

直接導(dǎo)致子線程未結(jié)束時候主線程已經(jīng)退出
下面的例子我們再利用thread的鎖機制來保證線程的同步

import thread
from time import sleep,ctime

loops = [4,2]

def loop(nloop,nsec,lock):
    print 'start loop',nloop, 'at:', ctime()
    sleep(nsec)
    print 'loop', nloop, 'done at:', ctime()
    lock.release()

def main():
    print 'starting at:', ctime()
    locks = []
    nloops = range(len(loops))

    for i in nloops:
        lock = thread.allocate_lock()
        lock.acquire()
        locks.append(lock)

    for i in nloops:
        thread.start_new_thread(loop,(i,loops[i],locks[i]))
        #sleep(1)

    for i in nloops:
        while locks[i].locked():
            pass

    print 'all done at:', ctime()

if __name__ == '__main__':
    main()

運行效果:

starting at: Thu Jun 08 17:05:59 2017
start loop 0 at: Thu Jun 08 17:05:59 2017start loop
 1 at: Thu Jun 08 17:05:59 2017
loop 1 done at: Thu Jun 08 17:06:01 2017
loop 0 done at: Thu Jun 08 17:06:03 2017
all done at: Thu Jun 08 17:06:03 2017

我們能看到兩個線程是并發(fā)的,并且主線程等子線程結(jié)束后才結(jié)束的。但是與書上不一致的是啟動線程中的輸出是亂掉了,不清楚為何和書上為何不一致。
根據(jù)作者的解釋建議我們盡量不要使用thread模塊,而使用更高級的threading模塊。
下面我們先看下threading模塊的對象:

threading模塊對象

利用threading模塊的thread類有三種方法:

? 創(chuàng)建Thread 的實例,傳給它一個函數(shù)。
? 創(chuàng)建Thread 的實例,傳給它一個可調(diào)用的類實例。
? 派生Thread 的子類,并創(chuàng)建子類的實例。

首先利用第一種方法來修改上面的例子:

? 創(chuàng)建Thread 的實例,傳給它一個函數(shù)。

import threading
from time import sleep, ctime

loops = [4, 2]


def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'loop', nloop, 'at:', ctime()


def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops))
    for i in nloops:
        t = threading.Thread(target=loop, args=(i, loops[i]))
        threads.append(t)
    for i in nloops:
        threads[i].start()
    for i in nloops:
        threads[i].join()
    print 'all done at:', ctime()


if __name__ == '__main__':
    main()

運行效果:跟之前的代碼對比,我們用一組thread對象代替了之前實現(xiàn)的鎖,并且代碼不會立即執(zhí)行,只在你希望它執(zhí)行的時候執(zhí)行;另外使用join()方法比等待鎖釋放的無限循環(huán)更清晰。

starting at: Fri Jun 09 10:21:45 2017
start loop 0 at: Fri Jun 09 10:21:45 2017
start loop 1 at: Fri Jun 09 10:21:45 2017
loop 1 at: Fri Jun 09 10:21:47 2017
loop 0 at: Fri Jun 09 10:21:49 2017
all done at: Fri Jun 09 10:21:49 2017

接著我們使用第二種方法來修改代碼

? 創(chuàng)建Thread 的實例,傳給它一個可調(diào)用的類實例。

import threading
from time import sleep, ctime

loops = [4, 2]


class ThreadFunc(object):
    def __init__(self, func, args, name=''):
        self.name = name
        self.func = func
        self.args = args

    def __call__(self):
        self.func(*self.args)

def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'loop', nloop, 'at:', ctime()

def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops))
    for i in nloops:
        t = threading.Thread(target=ThreadFunc(loop,(i,loops[i]),loop.__name__))
        threads.append(t)
    for i in nloops:
        threads[i].start()
    for i in nloops:
        threads[i].join()
    print 'all done at:', ctime()


if __name__ == '__main__':
    main()

運行結(jié)果:在上個代碼中添加一個新類ThreadFunc即得到這個代碼,比起一個函數(shù)有更好的靈活性,而不僅僅是單個函數(shù)

starting at: Fri Jun 09 10:31:34 2017
start loop 0 at: Fri Jun 09 10:31:34 2017
start loop 1 at: Fri Jun 09 10:31:34 2017
loop 1 at: Fri Jun 09 10:31:36 2017
loop 0 at: Fri Jun 09 10:31:38 2017
all done at: Fri Jun 09 10:31:38 2017

我們再看看第三種方式的代碼:

? 派生Thread 的子類,并創(chuàng)建子類的實例。

import threading
from time import sleep, ctime

loops = [4, 2]


class MyThread(threading.Thread):
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

def loop(nloop, nsec):
    print 'start loop', nloop, 'at:', ctime()
    sleep(nsec)
    print 'loop', nloop, 'at:', ctime()

def main():
    print 'starting at:', ctime()
    threads = []
    nloops = range(len(loops))
    for i in nloops:
        t = MyThread(loop,(i,loops[i]),loop.__name__)
        threads.append(t)
    for i in nloops:
        threads[i].start()
    for i in nloops:
        threads[i].join()
    print 'all done at:', ctime()


if __name__ == '__main__':
    main()

本例直接對Thread子類化,使我們具有更多的靈活性。
使用這種模式必須首先先調(diào)用基類的構(gòu)造函數(shù),即

基類構(gòu)造函數(shù)調(diào)用

還有之前的call()特殊方法必須寫為run()。
下面是運行結(jié)果:

starting at: Fri Jun 09 12:10:54 2017
start loop 0 at: Fri Jun 09 12:10:54 2017
start loop 1 at: Fri Jun 09 12:10:54 2017
loop 1 at: Fri Jun 09 12:10:56 2017
loop 0 at: Fri Jun 09 12:10:58 2017
all done at: Fri Jun 09 12:10:58 2017

如果我們要獲取子線程返回的結(jié)果怎么處理呢,下篇文章我們在討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 線程 引言&動機 考慮一下這個場景,我們有10000條數(shù)據(jù)需要處理,處理每條數(shù)據(jù)需要花費1秒,但讀取數(shù)據(jù)只需要0....
    不浪漫的浪漫_ea03閱讀 416評論 0 0
  • 引言&動機 考慮一下這個場景,我們有10000條數(shù)據(jù)需要處理,處理每條數(shù)據(jù)需要花費1秒,但讀取數(shù)據(jù)只需要0.1秒,...
    chen_000閱讀 587評論 0 0
  • 來源:數(shù)據(jù)分析網(wǎng)Threading 模塊從 Python 1.5.2 版開始出現(xiàn),用于增強底層的多線程模塊 thr...
    PyChina閱讀 1,796評論 0 5
  • 上一篇文章講了python多線程的基礎(chǔ)知識和thread模塊,這一篇著重講解一下threading模塊 threa...
    小奚有話說閱讀 1,800評論 0 1
  • 靜語輕楓閱讀 308評論 1 2

友情鏈接更多精彩內(nèi)容