Python基礎(8) 多線程

0. 前言

  • 目標:實現多線程併發執行程序。
  • 特點:
    • 由全局解釋器鎖於(Global Interpreter Lock,GIL)的存在,多線程執行程序只能佔用一個CPU核。
    • 換句話說,Python多線程不適用於CPU密集型任務,但適用於IO密集型任務
    • 當使用CPU密集型任務時,應該使用Python多進程
  • 相關包
    • threading 模塊
    • concurrent 模塊

1. threading 模塊

  • 主要是對單個線程的操作,即以 threading.Thread 爲基礎。

1.1. Thread 對象

  • 定義:threading.Thread(name="thread_name", daemon=None, target=None)
  • 主要方法:
    • start():啓動線程,會自動調用 run 方法。
    • run()
      • 一般自己實現實現線程時會重寫該方法。
      • 但在默認情況下,可以通過設置 target 參數來傳入需要執行的方法。
    • is_alive():線程是否還在運行中。
    • join(timeout=None):在A的線程中調用B線程的該方法,則A線程會等待B線程執行完畢後再進行下一步操作。
    • 守護進程相關:
      • isDaemon():判斷是否是守護進程。
      • daemon:作爲參數,可以直接get/set。
    • name:作爲參數,可以直接get/set。

1.2. 其他API

  • 包提供的方法:
    • active_count():活躍線程數量
    • current_thread():當前線程對象
    • get_ident():當前線程的idx,是一個非0數字
    • enumerate():遍歷獲取所有活躍線程
    • main_thread():主線程對象
  • 鎖對象:Lock,對應 acquire/release 方法。
  • 還有很多暫時用不到的,不記錄了。
    • RLock
    • Condition
    • Semaphore
    • Event
    • Timer
    • Barrier

2. concurrent.features 包

  • 不僅包含了線程池,也包含了進程池。
    • 本文不講進程池相關內容。

2.1. 線程池

  • Executor
    • 是個抽象類,不直接使用,一般都用他的子類。
    • 子類包括了線程池 ThreadPoolExecutor 與進程池 ProcessPoolExecutor
    • 主要方法:
      • submit(fn, *args, **kwargs):調用 fn(*args, **kwargs) 並返回 Future 對象。
      • shutdown(wait=True):關閉線程池。
      • map(func, *iterables, timeout=None, chunksize=1):類似於 map 方法,我自己不怎麼用所以不詳細瞭解了。
  • ThreadPoolExecutor
    • Executor 的子類,主要方法也就是上面的三個。
  • Future 對象:
    • 線程池調用 submit 方法後得到的。
    • 對象用來獲取提交的任務的運行狀態。
    • 常用方法:
      • cancel():取消任務,成功取消返回True,否則返回False。
      • cancelled():判斷是否取消。
      • running():判斷是否正在執行。
      • done():判斷是否運行結束(正常運行結束或cancel都算)。
      • result(timeout=None):獲取調用結果,如果還沒有運行結束則阻塞當前線程並等待結果,在規定時間內若還沒有運行結束,則會出發異常。
      • exception(timeout=None):類似於result,獲取調用引發的異常,正常運行結束則會返回None,若還沒有運行結束則會等待一定時間。

2. 實例

  • 線程池的創建/刪除可以手動實現,也可以通過 with 實現。
import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章