Python 多線程 -thread threading Queue- 簡單學習

 

Python 多線程 -thread threading Queue- 簡單學習html

在實際工做過程當中,會出現須要併發的作一些事情,例如一臺機器測到幾千臺機器的網絡連通性,若是你單線程一臺一臺測的話,會花費不少的事情,不具備實時性,更不能在變化的時候馬上感知當時網絡的情況,這時多線程就是一個很好地選擇。python已經給咱們封裝好了多線程庫thread和threading。python

thread:比較底層的模塊
threading:Higher-level threading interfacegit

ps:建議使用threading模塊
- 高級別的threading模塊更爲先進,對線程的支持更爲完善
- 低級別的thread模塊同步原語不多
- thread模塊對線程何時結束徹底沒有控制,當主線程結束時,全部線程都會強制結束github

thread

模塊函數web

start_new_thread(function, args,kwargs=None): 產生新的線程,args是function的參數,沒有時寫(),kwargs用來調用這個函數
allocate_lock(): 分配鎖,LockType類型
exit(): 讓線程退出canvas

LockType的操做ruby

acquire(wait=None):嘗試獲取鎖
locked(): 獲取了鎖返回True,沒有返回False
release():釋放鎖markdown

Demo1網絡

$ cat t1.py
import thread
from time import sleep

def a():
    print "a start"
    sleep(2)
    print "a end"

def b():
    print "b start"
    sleep(2)
    print "b end"

def main():
    thread.start_new_thread(a,())
    thread.start_new_thread(b,())
    print "all done"

if __name__ == "__main__":
    main()

$ python t1.py
all done
b start
a start

最終會發現,每一次運行出來的結果都有可能不一樣,可是絕對不會出現「a end」和「b end」。這是爲何呢,這裏沒有寫讓主線程停下來等全部子線程結束後再繼續運行的代碼,因此main線程在執行完print "all done"就關閉了a和b兩個線程。怎麼辦呢,能夠在這裏加一個sleep等待子進程執行完畢後再退出。多線程

Demo2: thread -- 多線程的演示 by sleep

$ cat t2.py
import thread
from time import sleep

def a():
    print "a start"
    sleep(2)
    print "a end"

def b():
    print "b start"
    sleep(2)
    print "b end"

def main():
    thread.start_new_thread(a,())
    thread.start_new_thread(b,())
    sleep (4)       ----防止主進程過早退出,加sleep等待子進程執行完畢後再推出
    print "all done"

if __name__ == "__main__":
    main()
$ python t1.py 
b start
a start
a end
b end
all done

可是假設咱們不知道子進程執行的時間怎麼辦,這就是鎖的用武之地了。由於使用鎖要比使用sleep()函數更爲合理。以下所示:

Demo3: thread -- 多線程演示 by lock

實現方式爲: 主線程初始化兩個鎖,分別傳給兩個函數,兩個函數在執行完本身的代碼後釋放鎖,主線程一直在輪詢這個鎖有沒有釋放,若是釋放了就退出。

def a(lock, nsec):
    print "a starting at :", ctime()
    sleep(nsec)
    lock.release()    -- 執行完以後釋放鎖
    print "a end", ctime()

def b(lock, nsec):
    print "b starting at :", ctime()
    sleep(nsec)
    lock.release()    -- 執行完以後釋放鎖
    print "b end", ctime()

def main():
    print "Demo Starting at:", ctime()

    locks = []

    # Initialize lock  -- 主線程先獲取兩個鎖,佔爲己有
    for i in range(2):
        lock = thread.allocate_lock()
        lock.acquire()
        locks.append(lock)

    # 每一個進程分配一個鎖
    thread.start_new_thread(a, (locks[0],2))
    thread.start_new_thread(b, (locks[1],4))

    for i in range(2):   #一直在輪詢,看鎖有沒有釋放
        while locks[i].locked(): pass 

    print "all done at:", ctime()

最後的結果爲:

$ python thread_demo.py 
Demo Starting at: Fri Aug 29 22:03:01 2014
a starting at : Fri Aug 29 22:03:01 2014
b starting at : Fri Aug 29 22:03:01 2014
a end Fri Aug 29 22:03:03 2014
b end Fri Aug 29 22:03:05 2014
all done at: Fri Aug 29 22:03:05 2014

不難發現,thread庫的同步機制比較難用,一切都須要主進程來處理。而且沒有守護進程,主進程一退,整個世界都會變得很清靜。而threading庫給咱們提供了守護進程。下面就來看看threading的簡單用法。

threading

threading提供了Thread類,還提供了不少很是好用的同步機制。感受重點了解Thread類就能夠,多線程,也就是經過Thread類的多個實例。 類的主要方法有:

start():開始線程的執行。thread庫裏裏面,是沒有辦法控制線程的開始的
join(timeout=None): 等待線程結束,有點相似Demo3中的輪詢
run():定義線程的功能

感受上面是比較重要的,立馬就會用到的。還有一些其餘的:

getName():獲取線程名
setName(name):設置線程名
isAlive(): 返回bool 表示線程是否在運行中
activeCount():返回運行中的線程數
currentThread():返回當前線程對象
enumerate():返回當前活動線程的列表
isDaemon(): 返回線程的Daemon標誌
setDaemon(daemonic): 設置線程的Daemon標誌,通常在start()函數前調用
settrace(func):爲全部線程設置跟蹤函數
setprofile(func): 爲全部線程設置profile函數

Demo4 -- threading演示

def loop(i, nsec):
    print "thread %d starting at : %s" %(i, ctime())
    sleep(nsec)
    print "thread %d end at : %s" %(i, ctime())

def main():
    threads = []
    loops = [2, 4]
    # 實例化進程
    for i in range(len(loops)):
        t = threading.Thread(target = loop, args = (i, loops[i]))
        threads.append(t)

    for i in range(len(loops)):
        threads[i].start()

    for i in range(len(loops)):
        threads[i].join()

    print "all done"

最後的結果爲:

thread 0 starting at : Sun Aug 31 13:31:28 2014
thread 1 starting at : Sun Aug 31 13:31:28 2014
thread 0 end at : Sun Aug 31 13:31:30 2014
thread 1 end at : Sun Aug 31 13:31:32 2014
all done

可見threading能夠方便的控制線程的開始,以及等待每一個線程的結束,而且也不用設置鎖,釋放鎖,這些都被threading庫封裝了,相比於thread要更高級一些。在實際的運維工程中,可能會須要多個線程執行相同的任務,這時須要一個任務池。每一個線程取任務池中取任務,執行,再取任務,再執行,一直到任務池爲空,退出線程。這裏就會用到下面要介紹的Queue庫。

Queue

Queue模塊能夠用來實現多線程間通信,讓各個線程共享數據,生產者把貨物放到Queue中,供消費者(線程)去使用。在python3中,Queue模塊被命名爲queue。 Queue的對象有:

Queue.Queue(maxsize=0): 建立大小爲maxsize的FIFO(First In First Out)-Queue對象,若是maxsize不設置,這個隊列將是無限的。
Queue.LifoQueue(maxsize=0): 建立先入後出的對象,即棧, 在python2.6中加入 Queue.PriorityQueue(maxsize=0):有優先級的隊列,在python2.6中加入

Queue對象的方法有:

qsize():返回隊列的大小
empty():返回隊列時候爲空
full():返回隊列是否滿
put(item,block=0):向Queue對象中放數據,block不爲0時,會一直等到隊列中有控件爲止
get(block=0):,同上,block不爲0時,會一直等到隊列中有數據爲止

Demo5 -- Queue的使用演示
場景: Queue裏面放着一些整數,須要將整數取出,而且睡眠整數大小的時間,下面的demo中,是放了10個1,若是單線程的話須要10s

def work(q):
    while True:
        if q.empty():
            return
        else:
            t = q.get()
            time.sleep(t)

def main():
    q = Queue.Queue()   # 初始化一個Queue對象
    for i in range(10): # 向Queue生產任務
        q.put(1)

    work(q)

if __name__ == "__main__":
    main()

最後的結果爲:

time python threading_demo2.py 

real    0m10.085s
user    0m0.060s
sys   0m0.004s  

單線程的話須要花費10s。

下面來經過多線程來處理Queue裏面的任務:

def work(q):
    while True:
        if q.empty():
            return
        else:
            t = q.get()
            time.sleep(t)

def main():
    q = Queue.Queue()
    for i in range(10):
        q.put(1)

    thread_num = 10
    threads = []
    for i in range(thread_num):
        t = threading.Thread(target = work, args = (q,)) # args須要輸出的是一個元組,若是隻有一個參數,後面加,表示元組,不然會報錯
        threads.append(t)

    for i in range(thread_num):
        threads[i].start()

    for i in range(thread_num):
        threads[i].join()

看看這下的結果爲:

real    0m1.046s
user    0m0.024s
sys     0m0.020s

所以對python多線程,主要學會使用threading和Queue,應該就能夠足以應付運維中的一些問題。

相關文章
相關標籤/搜索