Python 3多線程編程學習筆記-基礎篇

本文是學習《Python核心編程》的學習筆記,介紹了Python中的全局解釋器鎖和經常使用的兩個線程模塊:thread, threading,並對比他們的優缺點和給出簡單的列子。python

全局解釋器鎖(GIL)

Python代碼的執行都是有Python虛擬機進行控制的。當初設計Python的時候,考慮在主循環中只能有一個控制線程在執行,就像單核CPU進行多線程編程同樣。
怎麼作到這樣控制的呢?就是這裏的GIL來控制的,這個鎖用來保證同時只有一個線程在運行。編程

執行方式:服務器

Python 3多線程編程學習筆記-基礎篇

這幾個細節知識點:多線程

  1. 當調用外部代碼(C/C++擴展的內置函數)時, GIL會保持鎖定,知道函數執行結束
  2. 對於面向I/O的Python例程,GIL會在I/O調用前被釋放,從而容許其餘線程在I/O執行期間運行
  3. 若是是針對計算密集型的操做代碼,該線程整個時間片內更傾向於始終佔有處理器和GIL。

因此,針對Python虛擬機單線程(GIL)的設計緣由,只有線程在執行I/O密集型的應用,才能更好的發揮Python的併發性併發

對比thread,threading

Python多個模塊能夠進行多線程編程,包括:thread,threading等。他們均可以用來建立和管理線程。
thread模塊提供了基本的線程和鎖定支持,而threading模塊提供了更高級別,功能更全面的線程管理。app

推薦使用更高級別的threading模塊,下面是一個簡單的對比:ide

特徵要素 thread threading
功能全面性 基本(偏底層) 全面,高級
守護進程 不支持 支持
線程同步原語 僅1個(Lock) 不少(Lock,Semaphore,Barrier...)

守護進程講解

守護進程通常是一個等待客戶端請求服務的服務器。若是沒有客戶端請求,守護進程通常是空閒的。通常把一個線程設置成守護進程,就表示這個線程是不重要的。因此進程退出時是不須要等待這個守護線程完成的。函數

可是原先的thread 模塊是不區分守護或者非守護進程的,也就是說當主線程退出的時候,全部子線程都將終止,而無論他們是否仍在工做。若是你不想這種狀況發生,那麼就能夠採用threading模塊。整個Python程序(通常爲主線程)將在全部非守護進程退出是纔會退出。oop

設置守護進程,在線程啓動以前設置:
thread.daemon = True學習

多線程實踐

threading實例

方式1:建立一個thread實例,傳遞它一個函數

import threading
from time import sleep, ctime

sleep_times = [4, 2]

def loop(threadNo, sleep_time):
    print('Start loop', threadNo, 'at:', ctime())
    sleep(sleep_time)    #Sleep一段時間
    print('loop', threadNo, 'done at:', ctime())

def main():
    print('starting at:', ctime())
    threads = []
    threadIds = range(len(sleep_times))

    for i in threadIds:
        thread = threading.Thread(target=loop, args=(i,sleep_times[i]))
        threads.append(thread)

    for t in threads:
        # 依次啓動線程
        t.start()

    for t in threads:
        # 等待全部線程完成
        t.join() #將等待線程結束

    print('all Done at :', ctime())

if __name__ == '__main__':
    main()

方式2:派生Thread的子類,並建立子類的實例

import threading
from time import sleep, ctime

sleep_times = [4, 2]

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

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

def loop(threadNo, sleep_time):
    print('Start loop', threadNo, 'at:', ctime())
    sleep(sleep_time)  # Sleep一段時間
    print('loop', threadNo, 'done at:', ctime())

def main():
    print('starting at:', ctime())
    threads = []  # 用於存儲全部線程實例的列表
    threadIds = range(len(sleep_times))

    for i in threadIds:
        # 建立線程實例
        thread = MyThread(loop, (i, sleep_times[i]))
        threads.append(thread)

    for t in threads:
        # 依次啓動線程
        t.start()

    for t in threads:
        # 等待全部線程完成
        t.join()  # 將等待線程結束

    print('all Done at :', ctime())

if __name__ == '__main__':
    main()

thread實例

因爲本人使用的python3.6,這個thread已經變成_thread

import _thread
from time import sleep, ctime

sleep_times = [4, 2]

def loop(threadNo, sleep_time, lock):
    print('Start loop', threadNo, 'at:', ctime())
    sleep(sleep_time)    #Sleep一段時間
    print('loop', threadNo, 'done at:', ctime())
    lock.release()       #釋放鎖

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

    for i in threadIds:

        #經過調用_thread.allocate_lock得到鎖對象
        lock = _thread.allocate_lock()

        #經過acquire()方法取得鎖
        lock.acquire()
        locks.append(lock)

    for i in threadIds:
        # 依次啓動線程
        _thread.start_new_thread(loop, (i, sleep_times[i], locks[i]))

    for i in threadIds:
        # 若是當前的鎖Lock沒有釋放的話,一直循環等待
        while locks[i].locked():
            pass
    print('all Done at :', ctime())

if __name__ == '__main__':
    main()
相關文章
相關標籤/搜索