python 多線程編程之_thread模塊


參考書籍:python核心編程

_thread模塊除了可以派生線程外,還提供了基本的同步數據結構,又稱為鎖對象(lock object,也叫原語鎖、簡單鎖、互斥鎖、互斥和二進制信號量)。

下面是常用的線程函數:

函數 描述
start_new_thread(function,args,kwargs=None) 派生一個新的線程,使用給定的args和可選的kwargs來執行function
allocate_lock() 分配LockType對象
exit() 退出線程指令
LockType鎖對象的方法
acquire(wait=None) 嘗試獲取鎖對象
locked() 如果獲取了鎖對象則返回True,否則返回False
release() 釋放鎖

_thread模塊的核心函數是start_new_thread()。專門用來派生新的線程。

我們對上節文章的onethr.py文件稍作修改:

#!/usr/bin/env python

import _thread
from time import sleep,ctime

def loop0():
    print('開始循環0次在:',ctime())
    sleep(4)
    print('結束循環0次在:',ctime())

def loop1():
    print('開始循環1次在:',ctime())
    sleep(2)
    print('結束循環1次在:',ctime())

'''    
def main():
    print('開始於:',ctime())
    loop0()
    loop1()
    print('所有的任務都完成於:',ctime())
''' 

def main():
    print('starting at:', ctime())
    _thread.start_new_thread(loop0, ())
    _thread.start_new_thread(loop1, ())
    sleep(6)
    print('all done at:', ctime())
    
if __name__ =='__main__':
    main(

執行該腳本三遍,結果:

PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py
starting at: Mon Mar 26 21:56:10 2018
開始循環1次在: Mon Mar 26 21:56:10 2018
開始循環0次在: Mon Mar 26 21:56:10 2018
結束循環1次在: Mon Mar 26 21:56:12 2018
結束循環0次在: Mon Mar 26 21:56:14 2018
all done at: Mon Mar 26 21:56:16 2018
PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py
starting at: Mon Mar 26 22:00:43 2018
開始循環0次在: Mon Mar 26 22:00:43 2018
開始循環1次在: Mon Mar 26 22:00:43 2018
結束循環1次在: Mon Mar 26 22:00:45 2018
結束循環0次在: Mon Mar 26 22:00:47 2018
all done at: Mon Mar 26 22:00:49 2018
PS C:\Users\WC> python E:\Python3.6.3\workspace\mtsleepA.py
starting at: Mon Mar 26 22:00:56 2018
開始循環0次在: Mon Mar 26 22:00:56 2018
開始循環1次在: Mon Mar 26 22:00:56 2018
結束循環1次在: Mon Mar 26 22:00:58 2018
結束循環0次在: Mon Mar 26 22:01:00 2018
all done at: Mon Mar 26 22:01:02 2018

由上面的代碼可知,start_new_thread()必須包含兩個參數,即使要執行的函數不需要參數,也要傳遞一個空元組。

我們注意到:loop0還是loop1開始的順序竟然可以是無序的;loop0和loop1是同時執行的;loop1是在loop0之前結束的;整個程序一共耗時6秒。

我們可以說,loop0和loop1是並發執行的。

我們在主程序(其實也就是主線程)中增加了一個sleep(6)的語句,這其實是為了避免主程序結束的時候,loop0和loop1兩個線程還沒有結束的問題。這也是_thread模塊的一種線程同步機制。

但是,我們要說這樣使用sleep()來進行線程同步是不靠譜的,這也是_thread的一個弊端所在。

這時,我們可以引用鎖機制來實現相應的線程管理,並且同時改善單獨的循環函數實現方式:

import _thread
from time import sleep, ctime
#不再把4秒和2秒硬性的編碼到不同的函數中,而是使用唯一的loop()函數,並把這些常量放進列表loops中
loops=[4,2]
#代替了之前的loop*()函數,三個參數分別代表了處於第幾個循環中,睡眠時間和鎖對象。每個循環執行到最后一句的時候,釋放鎖對象,告訴主線程該線程已完成
def loop(nloop,sec,lock):
    print('開始循環',nloop,'在:',ctime())
    sleep(sec)
    print('循環',nloop ,'結束於:',ctime())
    lock.release()
    
def main():
    print('開始於:',ctime())
    locks=[]
    nloops=range(len(loops))
    
    #第一個for循環中,創建了一個鎖的列表,通過thread.allocate_lock()方法得到鎖對象,再通過acquire()方法取到鎖(相當於把鎖鎖上),取到之后就可以把它添加到鎖列表locks中。
    for i in nloops:
        lock=_thread.allocate_lock()
        lock.acquire()
        locks.append(lock)
    #第二個for循環中,主要用於派生線程。每個線程都會調用loop()函數,並傳遞循環號、睡眠時間以及用於該線程的鎖。  
    for i in nloops:
        _thread.start_new_thread(loop,(i,loops[i],locks[i]))
    #第三個for循環,按照順序檢查每個鎖。每個線程執行完畢后,都會釋放自己的鎖對象。這里使用忙等待,讓主線程等所有的鎖都釋放后才繼續執行
    for i in nloops:
        while locks[i].locked():
            pass
    print('所有的任務完成於:',ctime())
    
if __name__ =='__main__':
    main()

執行結果:

開始循環 1 在: Mon Mar 26 22:49:25 2018
開始循環 0 在: Mon Mar 26 22:49:25 2018
循環 1 結束於: Mon Mar 26 22:49:27 2018
循環 0 結束於: Mon Mar 26 22:49:29 2018
所有的任務完成於: Mon Mar 26 22:49:29 2018

上述結果除了表名兩次循環是並發執行的之外,整個程序一共用時4秒,而不是之前的6秒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM