
Python多線程與同步 (threading包)
Python主要通過標準庫中的threading包來實現(xiàn)多線程。在當今網(wǎng)絡(luò)時代,每個服務(wù)器都會接收到大量的請求。服務(wù)器可以利用多線程的方式來處理這些請求,以提高對網(wǎng)絡(luò)端口的讀寫效率。Python是一種網(wǎng)絡(luò)服務(wù)器的后臺工作語言 (比如豆瓣網(wǎng)),所以多線程也就很自然被Python語言支持。
(關(guān)于多線程的原理和C實現(xiàn)方法,請參考我之前寫的Linux多線程與同步,要了解race condition, mutex和condition variable的概念)
多線程售票以及同步
我們使用Python來實現(xiàn)Linux多線程與同步文中的售票程序。我們使用mutex (也就是Python中的Lock類對象) 來實現(xiàn)線程的同步:
# A program to simulate selling tickets in multi-thread way
# Written by Vamei
import threading
import time
import os
# This function could be any function to do other chores.
def doChore():
time.sleep(0.5)
# Function for each thread
def booth(tid):
global i
global lock
while True:
lock.acquire() # Lock; or wait if other thread is holding the lock
if i != 0:
i = i - 1 # Sell tickets
print(tid,':now left:',i) # Tickets left
doChore() # Other critical operations
else:
print("Thread_id",tid," No more tickets")
os._exit(0) # Exit the whole process immediately
lock.release() # Unblock
doChore() # Non-critical operations
# Start of the main function
i = 100 # Available ticket number
lock = threading.Lock() # Lock (i.e., mutex)
# Start 10 threads
for k in range(10):
new_thread = threading.Thread(target=booth,args=(k,)) # Set up thread; target: the callable (function) to be run, args: the argument for the callable
new_thread.start() # run the thread
我們使用了兩個全局變量,一個是i,用以儲存剩余票數(shù);一個是lock對象,用于同步線程對i的修改。此外,在最后的for循環(huán)中,我們總共設(shè)置了10個線程。每個線程都執(zhí)行booth()函數(shù)。線程在調(diào)用start()方法的時候正式啟動 (實際上,計算機中最多會有11個線程,因為主程序本身也會占用一個線程)。Python使用threading.Thread對象來代表線程,用threading.Lock對象來代表一個互斥鎖 (mutex)。
有兩點需要注意:
我們在函數(shù)中使用global來聲明變量為全局變量,從而讓多線程共享i和lock (在C語言中,我們通過將變量放在所有函數(shù)外面來讓它成為全局變量)。如果不這么聲明,由于i和lock是不可變數(shù)據(jù)對象,它們將被當作一個局部變量(參看Python動態(tài)類型)。如果是可變數(shù)據(jù)對象的話,則不需要global聲明。我們甚至可以將可變數(shù)據(jù)對象作為參數(shù)來傳遞給線程函數(shù)。這些線程將共享這些可變數(shù)據(jù)對象。
我們在booth中使用了兩個doChore()函數(shù)??梢栽谖磥砀倪M程序,以便讓線程除了進行i=i-1之外,做更多的操作,比如打印剩余票數(shù),找錢,或者喝口水之類的。第一個doChore()依然在Lock內(nèi)部,所以可以安全地使用共享資源 (critical operations, 比如打印剩余票數(shù))。第二個doChore()時,Lock已經(jīng)被釋放,所以不能再去使用共享資源。這時候可以做一些不使用共享資源的操作 (non-critical operation, 比如找錢、喝水)。我故意讓doChore()等待了0.5秒,以代表這些額外的操作可能花費的時間。你可以定義的函數(shù)來代替doChore()。
OOP創(chuàng)建線程
上面的Python程序非常類似于一個面向過程的C程序。我們下面介紹如何通過面向?qū)ο?(OOP, object-oriented programming,參看Python面向?qū)ο蟮幕靖拍詈蚉ython面向?qū)ο蟮倪M一步拓展) 的方法實現(xiàn)多線程,其核心是繼承threading.Thread類。我們上面的for循環(huán)中已經(jīng)利用了threading.Thread()的方法來創(chuàng)建一個Thread對象,并將函數(shù)booth()以及其參數(shù)傳遞給改對象,并調(diào)用start()方法來運行線程。OOP的話,通過修改Thread類的run()方法來定義線程所要執(zhí)行的命令。
復(fù)制代碼
# A program to simulate selling tickets in multi-thread way
# Written by Vamei
import threading
import time
import os
# This function could be any function to do other chores.
def doChore():
time.sleep(0.5)
# Function for each thread
class BoothThread(threading.Thread):
def __init__(self, tid, monitor):
self.tid = tid
self.monitor = monitor
threading.Thread.__init__(self)
def run(self):
while True:
monitor['lock'].acquire() # Lock; or wait if other thread is holding the lock
if monitor['tick'] != 0:
monitor['tick'] = monitor['tick'] - 1 # Sell tickets
print(self.tid,':now left:',monitor['tick']) # Tickets left
doChore() # Other critical operations
else:
print("Thread_id",self.tid," No more tickets")
os._exit(0) # Exit the whole process immediately
monitor['lock'].release() # Unblock
doChore() # Non-critical operations
# Start of the main function
monitor = {'tick':100, 'lock':threading.Lock()}
# Start 10 threads
for k in range(10):
new_thread = BoothThread(k, monitor)
new_thread.start()
復(fù)制代碼
我們自己定義了一個類BoothThread, 這個類繼承自thread.Threading類。然后我們把上面的booth()所進行的操作統(tǒng)統(tǒng)放入到BoothThread類的run()方法中。注意,我們沒有使用全局變量聲明global,而是使用了一個詞典monitor存放全局變量,然后把詞典作為參數(shù)傳遞給線程函數(shù)。由于詞典是可變數(shù)據(jù)對象,所以當它被傳遞給函數(shù)的時候,函數(shù)所使用的依然是同一個對象,相當于被多個線程所共享。這也是多線程乃至于多進程編程的一個技巧 (應(yīng)盡量避免上面的global聲明的用法,因為它并不適用于windows平臺)。
上面OOP編程方法與面向過程的編程方法相比,并沒有帶來太大實質(zhì)性的差別。
其他
threading.Thread對象: 我們已經(jīng)介紹了該對象的start()和run(), 此外:
join()方法,調(diào)用該方法的線程將等待直到改Thread對象完成,再恢復(fù)運行。這與進程間調(diào)用wait()函數(shù)相類似。
下面的對象用于處理多線程同步。對象一旦被建立,可以被多個線程共享,并根據(jù)情況阻塞某些進程。請與Linux多線程與同步中的同步工具參照閱讀。
threading.Lock對象: mutex, 有acquire()和release()方法。
threading.Condition對象: condition variable,建立該對象時,會包含一個Lock對象 (因為condition variable總是和mutex一起使用)??梢詫ondition對象調(diào)用acquire()和release()方法,以控制潛在的Lock對象。此外:
wait()方法,相當于cond_wait()
notify_all(),相當與cond_broadcast()
nofify(),與notify_all()功能類似,但只喚醒一個等待的線程,而不是全部
threading.Semaphore對象: semaphore,也就是計數(shù)鎖(semaphore傳統(tǒng)意義上是一種進程間同步工具,見Linux進程間通信)。創(chuàng)建對象的時候,可以傳遞一個整數(shù)作為計數(shù)上限 (sema = threading.Semaphore(5))。它與Lock類似,也有Lock的兩個方法。數(shù)據(jù)分析師培訓(xùn)
threading.Event對象: 與threading.Condition相類似,相當于沒有潛在的Lock保護的condition variable。對象有True和False兩個狀態(tài)。可以多個線程使用wait()等待,直到某個線程調(diào)用該對象的set()方法,將對象設(shè)置為True。線程可以調(diào)用對象的clear()方法來重置對象為False狀態(tài)。
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
LSTM 模型輸入長度選擇技巧:提升序列建模效能的關(guān)鍵? 在循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)家族中,長短期記憶網(wǎng)絡(luò)(LSTM)憑借其解決長序列 ...
2025-07-11CDA 數(shù)據(jù)分析師報考條件詳解與準備指南? ? 在數(shù)據(jù)驅(qū)動決策的時代浪潮下,CDA 數(shù)據(jù)分析師認證愈發(fā)受到矚目,成為眾多有志投身數(shù) ...
2025-07-11數(shù)據(jù)透視表中兩列相乘合計的實用指南? 在數(shù)據(jù)分析的日常工作中,數(shù)據(jù)透視表憑借其強大的數(shù)據(jù)匯總和分析功能,成為了 Excel 用戶 ...
2025-07-11尊敬的考生: 您好! 我們誠摯通知您,CDA Level I和 Level II考試大綱將于 2025年7月25日 實施重大更新。 此次更新旨在確保認 ...
2025-07-10BI 大數(shù)據(jù)分析師:連接數(shù)據(jù)與業(yè)務(wù)的價值轉(zhuǎn)化者? ? 在大數(shù)據(jù)與商業(yè)智能(Business Intelligence,簡稱 BI)深度融合的時代,BI ...
2025-07-10SQL 在預(yù)測分析中的應(yīng)用:從數(shù)據(jù)查詢到趨勢預(yù)判? ? 在數(shù)據(jù)驅(qū)動決策的時代,預(yù)測分析作為挖掘數(shù)據(jù)潛在價值的核心手段,正被廣泛 ...
2025-07-10數(shù)據(jù)查詢結(jié)束后:分析師的收尾工作與價值深化? ? 在數(shù)據(jù)分析的全流程中,“query end”(查詢結(jié)束)并非工作的終點,而是將數(shù) ...
2025-07-10CDA 數(shù)據(jù)分析師考試:從報考到取證的全攻略? 在數(shù)字經(jīng)濟蓬勃發(fā)展的今天,數(shù)據(jù)分析師已成為各行業(yè)爭搶的核心人才,而 CDA(Certi ...
2025-07-09【CDA干貨】單樣本趨勢性檢驗:捕捉數(shù)據(jù)背后的時間軌跡? 在數(shù)據(jù)分析的版圖中,單樣本趨勢性檢驗如同一位耐心的偵探,專注于從單 ...
2025-07-09year_month數(shù)據(jù)類型:時間維度的精準切片? ? 在數(shù)據(jù)的世界里,時間是最不可或缺的維度之一,而year_month數(shù)據(jù)類型就像一把精準 ...
2025-07-09CDA 備考干貨:Python 在數(shù)據(jù)分析中的核心應(yīng)用與實戰(zhàn)技巧? ? 在 CDA 數(shù)據(jù)分析師認證考試中,Python 作為數(shù)據(jù)處理與分析的核心 ...
2025-07-08SPSS 中的 Mann-Kendall 檢驗:數(shù)據(jù)趨勢與突變分析的有力工具? ? ? 在數(shù)據(jù)分析的廣袤領(lǐng)域中,準確捕捉數(shù)據(jù)的趨勢變化以及識別 ...
2025-07-08備戰(zhàn) CDA 數(shù)據(jù)分析師考試:需要多久?如何規(guī)劃? CDA(Certified Data Analyst)數(shù)據(jù)分析師認證作為國內(nèi)權(quán)威的數(shù)據(jù)分析能力認證 ...
2025-07-08LSTM 輸出不確定的成因、影響與應(yīng)對策略? 長短期記憶網(wǎng)絡(luò)(LSTM)作為循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的一種變體,憑借獨特的門控機制,在 ...
2025-07-07統(tǒng)計學(xué)方法在市場調(diào)研數(shù)據(jù)中的深度應(yīng)用? 市場調(diào)研是企業(yè)洞察市場動態(tài)、了解消費者需求的重要途徑,而統(tǒng)計學(xué)方法則是市場調(diào)研數(shù) ...
2025-07-07CDA數(shù)據(jù)分析師證書考試全攻略? 在數(shù)字化浪潮席卷全球的當下,數(shù)據(jù)已成為企業(yè)決策、行業(yè)發(fā)展的核心驅(qū)動力,數(shù)據(jù)分析師也因此成為 ...
2025-07-07剖析 CDA 數(shù)據(jù)分析師考試題型:解鎖高效備考與答題策略? CDA(Certified Data Analyst)數(shù)據(jù)分析師考試作為衡量數(shù)據(jù)專業(yè)能力的 ...
2025-07-04SQL Server 字符串截取轉(zhuǎn)日期:解鎖數(shù)據(jù)處理的關(guān)鍵技能? 在數(shù)據(jù)處理與分析工作中,數(shù)據(jù)格式的規(guī)范性是保證后續(xù)分析準確性的基礎(chǔ) ...
2025-07-04CDA 數(shù)據(jù)分析師視角:從數(shù)據(jù)迷霧中探尋商業(yè)真相? 在數(shù)字化浪潮席卷全球的今天,數(shù)據(jù)已成為企業(yè)決策的核心驅(qū)動力,CDA(Certifie ...
2025-07-04CDA 數(shù)據(jù)分析師:開啟數(shù)據(jù)職業(yè)發(fā)展新征程? ? 在數(shù)據(jù)成為核心生產(chǎn)要素的今天,數(shù)據(jù)分析師的職業(yè)價值愈發(fā)凸顯。CDA(Certified D ...
2025-07-03