python基礎執行緒-管理併發執行緒
執行緒模組建立線上程的底層特性之上,使執行緒的工作變得更簡單、更像python。使用執行緒允許程式在同一程式空間中併發執行多個操作。
很多人學習python,不知道從何學起。
很多人學習python,掌握了基本語法過後,不知道在哪裡尋找案例上手。
很多已經做案例的人,卻不知道如何去學習更加高深的知識。
那麼針對這三類人,我給大家提供一個好的學習平臺,免費領取視訊教程,電子書籍,以及課程的原始碼!
QQ群:961562169
執行緒物件
使用執行緒最簡單的方法是用目標函式例項化它,然後呼叫start()讓它開始工作。
import threading
def worker():
"""執行緒worker函式"""
print('Worker')
return
threads = []
for i in range(5):
t = threading.Thread(target=worker)
threads.append(t)
t.start()
結果:輸出為五行,每行上都有“Worker”
$ python3 threading_simple.py
Worker
Worker
Worker
Worker
Worker
能夠生成一個執行緒並傳遞引數來告訴它要做什麼工作是很有用的。這個例子傳遞一個數字,然後執行緒列印這個數字。
import threading
def worker(num):
"""執行緒worker函式"""
print('Worker: {num}' )
return
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
現在每個執行緒列印的訊息包含一個數字:
$ python3 -u threading_simpleargs.py
Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4
確定當前執行緒
使用引數來標識或命名執行緒很麻煩,而且沒有必要。每個Thread例項都有一個具有預設值的名稱,該名字可以在建立執行緒時更改。命名執行緒在具有多個服務執行緒來處理不同操作的伺服器程式中很有用。
import threading
import time
def worker():
print(threading.currentThread().getName(), '開始執行')
time.sleep(2)
print(threading.currentThread().getName(), '結束執行')
def my_service():
print(threading.currentThread().getName(), '開始執行')
time.sleep(3)
print(threading.currentThread().getName(), '結束執行')
t = threading.Thread(name='my_service', target=my_service)
w = threading.Thread(name='worker', target=worker)
w2 = threading.Thread(target=worker) # 使用預設名字
w.start()
w2.start()
t.start()
除錯輸出包括每行上當前執行緒的名稱。“執行緒名稱”列中帶有“Thread-1”的行對應於未命名的執行緒w2。
$ python -u threading_names.py
worker Thread-1 開始執行
my_service 開始執行
開始執行
Thread-1worker 結束執行
結束執行
my_service 結束執行
大多數程式不使用列印進行除錯。logging支援使用格式化程式程式碼%(threadName)在每個日誌訊息中嵌入執行緒名稱。在日誌訊息中包含執行緒名稱可以更容易地將這些訊息追溯到其源。
import logging
import threading
import time
logging.basicConfig(level=logging.DEBUG,
format='[%(levelname)s] (%(threadName)-10s) %(message)s',
)
def worker():
logging.debug('開始執行')
time.sleep(2)
logging.debug('結束執行')
def my_service():
logging.debug('開始執行')
time.sleep(3)
logging.debug('結束執行')
t = threading.Thread(name='my_service', target=my_service)
w = threading.Thread(name='worker', target=worker)
w2 = threading.Thread(target=worker) # 使用預設名字
w.start()
w2.start()
t.start()
logging是執行緒安全的,因此來自不同執行緒的訊息在輸出中保持不同。
$ python threading_names_log.py
[DEBUG] (worker ) 開始執行
[DEBUG] (Thread-1 ) 開始執行
[DEBUG] (my_service) 開始執行
[DEBUG] (worker ) 結束執行
[DEBUG] (Thread-1 ) 結束執行
[DEBUG] (my_service) 結束執行
守護程式與非守護程式執行緒
到目前為止,示例程式已隱式地等待退出,直到所有執行緒都完成了它們的工作。有時程式生成一個執行緒作為守護程式執行,而該執行緒在執行時不會阻止主程式退出。使用守護程式執行緒對於那些可能無法輕鬆中斷執行緒或讓執行緒在其工作過程中死亡不會丟失或損壞資料的服務(例如,為服務監視工具生成“心跳”的執行緒)非常有用。要將執行緒標記為守護程式,請使用布林引數呼叫其setDaemon()方法。預設情況下,執行緒不是守護程式,因此傳遞True將開啟守護程式模式。
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def daemon():
logging.debug('開始執行')
time.sleep(2)
logging.debug('結束執行')
d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)
def non_daemon():
logging.debug('開始執行')
logging.debug('結束執行')
t = threading.Thread(name='non-daemon', target=non_daemon)
d.start()
t.start()
請注意,輸出不包括來自守護程式執行緒的“結束執行”訊息,因為所有非守護程式執行緒(包括主執行緒)都在守護程式執行緒從其兩秒鐘的睡眠中喚醒之前退出。
$ python threading_daemon.py
(daemon ) 開始執行
(non-daemon) 開始執行
(non-daemon) 結束執行
要等到守護程式執行緒完成其工作,請使用join()方法。
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def daemon():
logging.debug('開始執行')
time.sleep(2)
logging.debug('結束執行')
d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)
def non_daemon():
logging.debug('開始執行')
logging.debug('結束執行')
t = threading.Thread(name='non-daemon', target=non_daemon)
d.start()
t.start()
d.join()
t.join()
等待守護執行緒使用join()退出意味著它有機會生成“結束執行”訊息。
$ python threading_daemon_join.py
(daemon ) 開始執行
(non-daemon) 開始執行
(non-daemon) 結束執行
(daemon ) 結束執行
預設情況下,join()無限期阻塞。也可以傳遞一個超時引數(一個浮點數,表示等待執行緒變為非活動狀態的秒數)。如果執行緒沒有在超時時間內完成,join()仍然返回。
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def daemon():
logging.debug('開始執行')
time.sleep(2)
logging.debug('結束執行')
d = threading.Thread(name='daemon', target=daemon)
d.setDaemon(True)
def non_daemon():
logging.debug('開始執行')
logging.debug('結束執行')
t = threading.Thread(name='non-daemon', target=non_daemon)
d.start()
t.start()
d.join(1)
# python2寫法
print ('d.isAlive()', d.isAlive())
# python3寫法
print ('d.is_alive()', d.is_alive())
t.join()
由於傳遞的超時值小於守護程式執行緒的睡眠時間,因此join()返回後,執行緒仍然是“活動的”。
$ python threading_daemon_join_timeout.py
(daemon ) 開始執行
(non-daemon) 開始執行
(non-daemon) 結束執行
d.isAlive() True
d.is_alive() True
列舉所有執行緒
沒有必要保留所有守護程式執行緒的顯式控制程式碼,以確保它們在退出主程式之前已完成。enumerate()返回活動執行緒例項的列表。該列表包含當前執行緒,並且由於不允許加入當前執行緒(這會導致死鎖情況),因此必須跳過它。
import random
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def worker():
"""執行緒worker函式"""
t = threading.currentThread()
pause = random.randint(1,5)
logging.debug('sleeping %s', pause)
time.sleep(pause)
logging.debug('ending')
return
for i in range(3):
t = threading.Thread(target=worker)
t.setDaemon(True)
t.start()
main_thread = threading.currentThread()
for t in threading.enumerate():
if t is main_thread:
continue
logging.debug(f'joining { t.getName()}')
t.join()
由於worker睡眠的時間是隨機的,所以這個程式的輸出可能會有所不同。應該是這樣的:
$ python threading_enumerate.py
(Thread-1 ) sleeping 3
(Thread-2 ) sleeping 2
(Thread-3 ) sleeping 5
(MainThread) joining Thread-1
(Thread-2 ) ending
(Thread-1 ) ending
(MainThread) joining Thread-3
(Thread-3 ) ending
(MainThread) joining Thread-
子類化執行緒
在啟動時,執行緒進行一些基本的初始化,然後呼叫其run()方法,該方法呼叫傳遞給建構函式的目標函式。若要建立Thread的子類,請重寫run()以執行任何必要的操作。
import threading
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
class MyThread(threading.Thread):
def run(self):
logging.debug('running')
return
for i in range(5):
t = MyThread()
t.start()
忽略run()的返回值。
$ python threading_subclass.py
(Thread-1 ) running
(Thread-2 ) running
(Thread-3 ) running
(Thread-4 ) running
(Thread-5 ) running
因為傳遞給Thread建構函式的args和kwargs值儲存在私有變數中,因此不容易從子類訪問它們。要將引數傳遞給自定義執行緒型別,請重新定義建構函式以將值儲存在例項屬性中,該屬性可以在子類中看到。
import threading
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
class MyThreadWithArgs(threading.Thread):
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, verbose=None):
threading.Thread.__init__(self, group=group, target=target, name=name,
verbose=verbose)
self.args = args
self.kwargs = kwargs
return
def run(self):
logging.debug('running with %s and %s', self.args, self.kwargs)
return
for i in range(5):
t = MyThreadWithArgs(args=(i,), kwargs={'a':'A', 'b':'B'})
t.start()
MyThreadWithArgs與Thread使用相同的API,但是與其他任何類一樣,另一個類可以輕鬆地更改建構函式方法以採用與執行緒目的更直接相關的更多或不同的引數。
$ python threading_subclass_args.py
(Thread-1 ) running with (0,) and {'a': 'A', 'b': 'B'}
(Thread-2 ) running with (1,) and {'a': 'A', 'b': 'B'}
(Thread-3 ) running with (2,) and {'a': 'A', 'b': 'B'}
(Thread-4 ) running with (3,) and {'a': 'A', 'b': 'B'}
(Thread-5 ) running with (4,) and {'a': 'A', 'b': 'B'}
計時器執行緒
Timer提供了一個將Thread子類化的原因的示例,它也包含線上程中。計時器在延遲之後開始工作,並且可以在該延遲時間段內的任何時間取消。
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def delayed():
logging.debug('worker running')
return
t1 = threading.Timer(3, delayed)
t1.setName('t1')
t2 = threading.Timer(3, delayed)
t2.setName('t2')
logging.debug('starting timers')
t1.start()
t2.start()
logging.debug('waiting before canceling %s', t2.getName())
time.sleep(2)
logging.debug('canceling %s', t2.getName())
t2.cancel()
logging.debug('done')
請注意,第二個計時器從不執行,而第一個計時器似乎在主程式的其餘部分完成後執行。因為它不是守護程式執行緒,所以當主執行緒完成時,它是隱式連線的。
$ python threading_timer.py
(MainThread) starting timers
(MainThread) waiting before canceling t2
(MainThread) canceling t2
(MainThread) done
(t1 ) worker running
執行緒間信令
雖然使用多個執行緒的目的是分離出單獨的操作以併發執行,但有時能夠在兩個或多個執行緒中同步這些操作是很重要的。執行緒之間通訊的一個簡單方法是使用事件物件。事件管理內部標誌,呼叫者可以set()或clear()。其他執行緒可以wait()設定set(),有效地阻止程式,直到允許繼續。
import logging
import threading
import time
logging.basicConfig(level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
def wait_for_event(e):
"""Wait for the event to be set before doing anything"""
logging.debug('wait_for_event starting')
event_is_set = e.wait()
logging.debug('event set: %s', event_is_set)
def wait_for_event_timeout(e, t):
"""Wait t seconds and then timeout"""
while not e.isSet():
logging.debug('wait_for_event_timeout starting')
event_is_set = e.wait(t)
logging.debug('event set: %s', event_is_set)
if event_is_set:
logging.debug('processing event')
else:
logging.debug('doing other work')
e = threading.Event()
t1 = threading.Thread(name='block',
target=wait_for_event,
args=(e,))
t1.start()
t2 = threading.Thread(name='non-block',
target=wait_for_event_timeout,
args=(e, 2))
t2.start()
logging.debug('Waiting before calling Event.set()')
time.sleep(3)
e.set()
logging.debug('Event is set')
wait()方法在等待表示時間的引數之前佔用了秒數。它返回一個布林值,指示是否設定了事件,因此呼叫者知道wait()返回的原因。isSet()方法可以單獨用於事件,而不必擔心阻塞。
在本例中,wait_for_event_timeout()檢查事件狀態,不會無限期阻塞。wait_for_event()會阻塞對wait()的呼叫,直到事件狀態更改後才會返回。
$ python threading_event.py
(block ) wait_for_event starting
(non-block ) wait_for_event_timeout starting
(MainThread) Waiting before calling Event.set()
(non-block ) event set: False
(non-block ) doing other work
(non-block ) wait_for_event_timeout starting
(MainThread) Event is set
(block ) event set: True
(non-block ) event set: True
(non-block ) processing event
相關文章
- 【多執行緒與高併發】- 執行緒基礎與狀態執行緒
- 併發與多執行緒基礎執行緒
- JAVA多執行緒和併發基礎Java執行緒
- Java併發基礎(2)------執行緒池Java執行緒
- iOS開發基礎——執行緒安全(執行緒鎖)iOS執行緒
- python多執行緒基礎Python執行緒
- 程式執行緒篇——程式執行緒基礎執行緒
- Java執行緒池一:執行緒基礎Java執行緒
- C#多執行緒開發-執行緒基礎 01C#執行緒
- 執行緒基礎執行緒
- 併發程式設計之多執行緒基礎程式設計執行緒
- Java併發程式設計-執行緒基礎Java程式設計執行緒
- 多執行緒併發篇——如何停止執行緒執行緒
- 多執行緒--執行緒管理執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- 多執行緒系列(1),多執行緒基礎執行緒
- 多執行緒系列(三):執行緒池基礎執行緒
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- 66.QT-執行緒併發、QTcpServer併發、QThreadPool執行緒池QT執行緒TCPServerthread
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 玩轉java多執行緒 之多執行緒基礎 執行緒狀態 及執行緒停止實戰Java執行緒
- 多執行緒學習一(多執行緒基礎)執行緒
- Java 多執行緒基礎(八)執行緒讓步Java執行緒
- 一. 執行緒管理之Thread基礎執行緒thread
- Python《多執行緒併發爬蟲》Python執行緒爬蟲
- Java併發(四)----執行緒執行原理Java執行緒
- 多執行緒基礎執行緒
- Java 執行緒基礎Java執行緒
- java多執行緒與併發 - 執行緒池詳解Java執行緒
- Java併發實戰一:執行緒與執行緒安全Java執行緒
- Java併發指南1:併發基礎與Java多執行緒Java執行緒
- 二. 執行緒管理之執行緒池執行緒
- c#基礎,單執行緒,跨執行緒訪問和執行緒帶引數C#執行緒
- Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒Java執行緒
- 執行緒基礎(二十一)-併發容器-ArrayBlockingQueue(上)執行緒BloC
- Java多執行緒與併發基礎面試題Java執行緒面試題