莆田 網(wǎng)站建設(shè)今日最新新聞重大事件
文章目錄
- 前言
- 多進(jìn)程與多線程
- 基本概念
- 多進(jìn)程
- multiprocessing 類對象
- 進(jìn)程池
- subprocess模塊
- 進(jìn)程間通信
- 多線程
- threading實現(xiàn)線程操作
- 線程共享所有變量
- 線程鎖
- 參考資料
前言
又花了點時間學(xué)習(xí)了一下Python中的多線程與多進(jìn)程的知識點,梳理一下供復(fù)習(xí)參考
多進(jìn)程與多線程
基本概念
進(jìn)程指的是程序的一次執(zhí)行,它是系統(tǒng)資源分配的單位,不同進(jìn)程間的資源互相獨立,但是系統(tǒng)開銷較大
線程是進(jìn)程的執(zhí)行單元,它是CPU調(diào)度的基本單位,線程能夠共享進(jìn)程的資源,它的優(yōu)點是效率高,缺點是會影響所在的進(jìn)程
多進(jìn)程
multiprocessing 類對象
Python中的多進(jìn)程常用multiprocessing庫實現(xiàn),支持跨平臺的多進(jìn)程操作,一個實例如下:
from multiprocessing import Process
import os
import time
def run_proc(name):print('子進(jìn)程 %s PID: %s 已經(jīng)啟動...' % (name,os.getpid()))time.sleep(5)print('子進(jìn)程 %s PID: %s 終止...' % (name,os.getpid()))if __name__ == '__main__':print('父進(jìn)程PID: %s' % (os.getpid()))p=Process(target=run_proc,args=('test',))print('子進(jìn)程即將啟動...')p.start() #啟動進(jìn)程,并調(diào)用該子進(jìn)程中的p.run()p.join() #阻塞當(dāng)前所在進(jìn)程,等待所有進(jìn)程退出 #嘗試注釋此行觀察程序執(zhí)行變化print('主進(jìn)程終止...')
- 一個Processs對象就代表一個進(jìn)程對象,傳入的函數(shù)名及參數(shù)作為進(jìn)程對象的參數(shù)
- 使用start方法啟動進(jìn)程對象,默認(rèn)調(diào)用子進(jìn)程的run()方法
- join方法表示等待進(jìn)程結(jié)束,此實例中用p.join()表示主進(jìn)程阻塞,等待子進(jìn)程執(zhí)行推出后再繼續(xù)執(zhí)行
進(jìn)程池
利用進(jìn)程池運行進(jìn)程的實例如下:
from multiprocessing import Pool
import os,time,randomdef long_time_task(name):print('子進(jìn)程 %s 啟動, PID: %s' % (name, os.getpid()) )stime=time.time()# time.sleep(random.random()*3)time.sleep(1)etime=time.time()print('子進(jìn)程 %s 運行結(jié)束,耗時: %f' % (name,(etime-stime)))if __name__ == '__main__':print('主進(jìn)程啟動,PID:',os.getpid())stime=time.time()p=Pool(2) #Pool()中的參數(shù)表示最多能夠同時運行幾個進(jìn)程,其余進(jìn)程需要等到已運行進(jìn)程結(jié)束后才能運行for i in range(6):p.apply_async(long_time_task,args=(i,))print('等待所有子進(jìn)程運行結(jié)束...')p.close()p.join()etime=time.time()print('所有子進(jìn)程運行結(jié)束,共耗時:',(etime-stime))
執(zhí)行結(jié)果:
解釋:
- 每次同時運行兩個進(jìn)程,每個進(jìn)程執(zhí)行時間約1秒,共有6個進(jìn)程,因此執(zhí)行時間共為3秒
- 如果注釋掉 join()的話主進(jìn)程會直接結(jié)束,看不到子進(jìn)程的輸出
subprocess模塊
subprocess 模塊允許我們啟動一個新進(jìn)程,并連接到它們的輸入/輸出/錯誤管道,從而獲取返回值。
其中的subprocess.call()則可以調(diào)用windows系統(tǒng)cmd命令行執(zhí)行額外的命令。
import subprocessprint('利用subprocess查詢DNS...')
r=subprocess.call(['nslookup','baidu.com'])
print('返回狀態(tài)碼',r)
執(zhí)行結(jié)果
解釋:利用subprocess.call()類似于我們開啟了一個新的命令窗口(新的進(jìn)程),輸入call()方法的參數(shù),同時將命令執(zhí)行結(jié)果的輸出返回到當(dāng)前進(jìn)程的輸出
進(jìn)程間通信
multiprocessing模塊提供了隊列、管道等方式幫助進(jìn)程之間交換數(shù)據(jù)
以下例子創(chuàng)建了兩個進(jìn)程,read進(jìn)程向隊列中讀取數(shù)據(jù),write進(jìn)程向隊列中寫入數(shù)據(jù)
from multiprocessing import Process,Queue
import os,time,randomdef write(q):print('write進(jìn)程啟動,PID:',os.getpid())for value in ['A','B','C','D']:print('將',value,'放入隊列')q.put(value)time.sleep(1)def read(q):print('read進(jìn)程啟動,PID:',os.getpid())while True:value=q.get()print('從隊列中取出',value)pass
執(zhí)行結(jié)果:
多線程
- Python支持真正的多線程
- 通常用_thread和threading兩個模塊實現(xiàn)Python多線程,后者更為常用
- 線程執(zhí)行的目標(biāo)是函數(shù),可以為線程命名
threading實現(xiàn)線程操作
import time,threadingdef lp():print('線程',threading.current_thread().name,'正在運行')for i in range(3):print('線程進(jìn)入第',i+1,'次循環(huán)')time.sleep(1)print('線程',threading.current_thread().name,'終止')print('線程',threading.current_thread().name,'正在運行')
t=threading.Thread(target=lp,name='子線程')
t.start()
t.join()
print('線程',threading.current_thread().name,'終止')
執(zhí)行結(jié)果
線程共享所有變量
以下是一個簡單的例子,利用add和sub線程對共享變量進(jìn)行修改
import time,threading
share=1000
def add():global shareshare+=5print('線程',threading.current_thread().name,'正在運行,變量share自增, share:',share)time.sleep(1)def sub():global shareshare-=2print('線程',threading.current_thread().name,'正在運行,變量share自減, share:',share)time.sleep(1)t1=threading.Thread(target=add,name='add')
t2=threading.Thread(target=sub,name='sub')
t1.start()
t2.start()
t1.join()
t2.join()
print('share:',share)
執(zhí)行結(jié)果:
線程鎖
讓我們嘗試修改一下上面的例子,add和sub線程隨機修改三次共享變量share,沒有線程鎖時將出現(xiàn)混亂,我們無法預(yù)測哪個線程先對share變量進(jìn)行修改,程序每次運行的結(jié)果可能會有不一樣:
import time,threading,randomshare=1000
lock=threading.Lock()def add():global share#隨機自增三次for i in range(3):share+=random.randint(1,10)print('線程',threading.current_thread().name,'正在運行,變量share隨機自增, share:',share)time.sleep(random.random())def sub():global share#隨機自減三次for i in range(3):share-=random.randint(1,10)print('線程',threading.current_thread().name,'正在運行,變量share隨機自減, share:',share)time.sleep(random.random())t1=threading.Thread(target=add,name='add')
t2=threading.Thread(target=sub,name='sub')
t1.start()
t2.start()
t1.join()
t2.join()
print('share:',share)
運行結(jié)果:
加入線程鎖之后,就可以等到某個線程執(zhí)行結(jié)束后再執(zhí)行另一個線程,不會出現(xiàn)交替執(zhí)行的情況
import time,threading,randomshare=1000
lock=threading.Lock()def add():global share#隨機自增三次try:lock.acquire()for i in range(3):share+=random.randint(1,10)print('線程',threading.current_thread().name,'正在運行,變量share隨機自增, share:',share)time.sleep(random.random())finally:lock.release()def sub():global share#隨機自減三次try:lock.acquire()for i in range(3):share-=random.randint(1,10)print('線程',threading.current_thread().name,'正在運行,變量share隨機自增, share:',share)time.sleep(random.random())finally:lock.release()t1=threading.Thread(target=add,name='add')
t2=threading.Thread(target=sub,name='sub')
t1.start()
t2.start()
t1.join()
t2.join()
print('share:',share)
執(zhí)行結(jié)果:
參考資料
Python正則表達(dá)式
Python多進(jìn)程與多線程
subprocess.call()