Python中嘗試多線程編程的一個(gè)簡(jiǎn)明例子
綜述
多線程是程序設(shè)計(jì)中的一個(gè)重要方面,尤其是在服務(wù)器Deamon程序方面。無論何種系統(tǒng),線程調(diào)度的開銷都比傳統(tǒng)的進(jìn)程要快得多。
Python可以方便地支持多線程??梢钥焖賱?chuàng)建線程、互斥鎖、信號(hào)量等等元素,支持線程讀寫同步互斥。美中不足的是,Python的運(yùn)行在Python 虛擬機(jī)上,創(chuàng)建的多線程可能是虛擬的線程,需要由Python虛擬機(jī)來輪詢調(diào)度,這大大降低了Python多線程的可用性。希望高版本的Python可以 解決這個(gè)問題,發(fā)揮多CPU的最大效率。
網(wǎng)上有些朋友說要獲得真正多CPU的好處,有兩種方法:
1.可以創(chuàng)建多個(gè)進(jìn)程而不是線程,進(jìn)程數(shù)和cpu一樣多。
2.使用Jython 或 IronPython,可以得到真正的多線程。
閑話少說,下面看看Python如何建立線程
Python線程創(chuàng)建
使用threading模塊的 Thread類
類接口如下
需要關(guān)注的參數(shù)是target和args. target 是需要子線程運(yùn)行的目標(biāo)函數(shù),args是函數(shù)的參數(shù),以tuple的形式傳遞。
以下代碼創(chuàng)建一個(gè)指向函數(shù)worker 的子線程
...
th = threading.Thread(target=worker,args=(i,acc) ) ;
啟動(dòng)這個(gè)線程
等待線程返回
或者th.join()
如果你可以對(duì)要處理的數(shù)據(jù)進(jìn)行很好的劃分,而且線程之間無須通信,那么你可以使用:創(chuàng)建=》運(yùn)行=》回收的方式編寫你的多線程程序。但是如果線程之間需要訪問共同的對(duì)象,則需要引入互斥鎖或者信號(hào)量對(duì)資源進(jìn)行互斥訪問。
下面講講如何創(chuàng)建互斥鎖
創(chuàng)建鎖
....
使用鎖
#鎖定,從下一句代碼到釋放前互斥訪問
g_mutex.acquire()
a_account.deposite(1)
#釋放
g_mutex.release()
最后,模擬一個(gè)公交地鐵IC卡繳車費(fèi)的多線程程序
有10個(gè)讀卡器,每個(gè)讀卡器收費(fèi)器每次扣除用戶一塊錢進(jìn)入總賬中,每讀卡器每天一共被刷10000000次。賬戶原有100塊。所以最后的總賬應(yīng)該為10000100。先不使用互斥鎖來進(jìn)行鎖定(注釋掉了鎖定代碼),看看后果如何。
import time,datetime
import threading
def worker(a_tid,a_account):
global g_mutex
print("Str " , a_tid, datetime.datetime.now() )
for i in range(1000000):
#g_mutex.acquire()
a_account.deposite(1)
#g_mutex.release()
print("End " , a_tid , datetime.datetime.now() )
class Account:
def __init__ (self, a_base ):
self.m_amount=a_base
def deposite(self,a_amount):
self.m_amount+=a_amount
def withdraw(self,a_amount):
self.m_amount-=a_amount
if __name__ == "__main__":
global g_mutex
count = 0
dstart = datetime.datetime.now()
print("Main Thread Start At: ", dstart)
#init thread_pool
thread_pool = []
#init mutex
g_mutex = threading.Lock()
# init thread items
acc = Account(100)
for i in range(10):
th = threading.Thread(target=worker,args=(i,acc) ) ;
thread_pool.append(th)
# start threads one by one
for i in range(10):
thread_pool[i].start()
#collect all threads
for i in range(10):
threading.Thread.join(thread_pool[i])
dend = datetime.datetime.now()
print("count=", acc.m_amount)
print("Main Thread End at: ", dend, " time span ", dend-dstart)
注意,先不用互斥鎖進(jìn)行臨界段訪問控制,運(yùn)行結(jié)果如下:

從結(jié)果看到,程序確實(shí)是多線程運(yùn)行的。但是由于沒有對(duì)對(duì)象Account進(jìn)行互斥訪問,所以結(jié)果是錯(cuò)誤的,只有3434612,比原預(yù)計(jì)少了很多。
打開鎖后:

這次可以看到,結(jié)果正確了。運(yùn)行時(shí)間比不進(jìn)行互斥多了很多,不過這也是同步的代價(jià)。
同時(shí)發(fā)現(xiàn),寫多線程,多進(jìn)程類的程序,不能用自帶的idle來運(yùn)行。會(huì)有錯(cuò)誤。
相關(guān)文章
詳解PyQt5 GUI 接收UDP數(shù)據(jù)并動(dòng)態(tài)繪圖的過程(多線程間信號(hào)傳遞)
這篇文章主要介紹了PyQt5 GUI 接收UDP數(shù)據(jù)并動(dòng)態(tài)繪圖(多線程間信號(hào)傳遞),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Pycharm中import torch報(bào)錯(cuò)的快速解決方法
這篇文章主要介紹了Pycharm中import torch報(bào)錯(cuò)的快速解決方法,很多朋友容易碰到這個(gè)問題,今天小編特此把解決方案分享到腳本之家平臺(tái)供大家參考,需要的朋友可以參考下2020-03-03
Python 函數(shù)繪圖及函數(shù)圖像微分與積分
今天小編就為大家分享一篇Python 函數(shù)繪圖及函數(shù)圖像微分與積分,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-11-11
Keras預(yù)訓(xùn)練的ImageNet模型實(shí)現(xiàn)分類操作
這篇文章主要介紹了Keras預(yù)訓(xùn)練的ImageNet模型實(shí)現(xiàn)分類操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Django上傳xlsx文件直接轉(zhuǎn)化為DataFrame或直接保存的方法
這篇文章主要介紹了Django上傳xlsx文件直接轉(zhuǎn)化為DataFrame或直接保存的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05

