Concurrency 併發, 可以看做是 Thread (I/O-bound)
當我們需要等待的時候(像是等待網路上的 response), 這時候我們可以先去做別的事情.
Parallelism 並行, 可以看做是 Process (CPU-bound)
一次開很多個人同時完成, 就像是大量的影分身.
主要使用在 I/O bound, 像是爬蟲或寫資料這種需要等待的類型.
你會發現輸出如下
> python3 demo_threading_non_join.py
開始執行 1
開始執行 2
開始執行 3
main thread quit
結束 3
結束 1
結束 2
還沒有執行結束, 就執行了 main thread quit.
> python3 demo_threading_join.py
開始執行 1
開始執行 2
開始執行 3
結束 2
結束 3
結束 1
main thread quit
當加入 join, 程式就會等到全部執行完才接著往下執行,
可以看到 main thread quit 顯示在最後.
當主要的 thread 退出的時候, 你會發現 main()
持續在執行,
除非你 ctrl+c
才會中斷,
執行 demo_threading_non_daemon.py
> python3 demo_threading_non_daemon.py
執行中...
isDaemon: False
執行中...
執行中...
執行中...
執行中...
執行中...
main thread quit
執行中...
執行中...
......
當我們使用 daemon 就不一樣了, 當主要的 thread 退出的時候,
整個 thread 就會被停止了
> python3 demo_threading_daemon.py
daemon Thread
執行中...
isDaemon: True
執行中...
執行中...
執行中...
執行中...
main thread quit
這邊再給一個範例, 利用 daemon 持續更新或監控某件事情
import threading
import time
def keep_updates():
"""
保持更新
"""
print('run keep_updates')
count = 0
while True:
try:
count += 1
# do somethings......
print(count)
time.sleep(0.5)
except Exception:
print('Exception')
time.sleep(10)
if __name__ == "__main__":
monitor_thread = threading.Thread(target=keep_updates, daemon=True)
monitor_thread.start()
# 等待主 thread 結束, 避免退出
monitor_thread.join()
當同時有很多個 Thread 要用到同一個資料時,
為了不發生 Race Condition 的現象,
需要使用 lock.acquire()
以及 lock.release()
來將它鎖定,
不要讓其他 Thread 執行.
先來看一個 Race Condition 的例子,
兩個 thread 分別加減 3, 多執行幾次,
有時後會出現很奇怪的現象(如下), 這就是戶搶的結果.
> python3 demo_threading_non_lock.py
This is the first thread 1
This is the second thread 0
This is the second thread 0
This is the first thread 1
This is the second thread 0
This is the first thread 1
透過 lock 改善這段 code,
不管執行幾次, 結果永遠是正確的輸出,
> python3 demo_threading_lock.py
This is the first thread 1
This is the first thread 2
This is the first thread 3
This is the second thread 2
This is the second thread 1
This is the second thread 0
主要使用在 CPU bound, 大量使用 CPU 的情境.
基本上, 使用上和 thread 大同小異.
你會發現輸出如下
> python3 demo_process_non_join.py
開始執行 ['www.yahoo.com.tw, www.google.com']
main process quit
開始執行 ['www.yahoo.com.tw, www.google.com']
開始執行 ['www.yahoo.com.tw, www.google.com']
結束 1
結束 2
結束 3
還沒有執行結束, 就執行了 main process quit.
如果執行 demo_process_join.py
> python3 demo_process_join.py
開始執行 ['www.yahoo.com.tw, www.google.com']
開始執行 ['www.yahoo.com.tw, www.google.com']
開始執行 ['www.yahoo.com.tw, www.google.com']
結束 2
結束 1
結束 3
main process quit
當加入 join, 程式就會等到全部執行完才接著往下執行,
可以看到 main process quit 顯示在最後.
一般除了看到 import threading
import multiprocessing
之外,
還有更高階的套件, 就是 concurrent.futures
,
請參考 concurrent_futures_tutorial
至於要使用 import threading
import multiprocessing
,
還是 concurrent.futures
這部份, 基本上都可以,
concurrent.futures
學習成本低一點 😄
( 相對 import threading
import multiprocessing
來說, 細節比較少 ).