1.過程這個概念
過程,有時候被稱作輕量級進(jìn)程(LightweightProcess,LWP),是程序執(zhí)行流的最低控制模塊。統(tǒng)一標(biāo)準(zhǔn)的過程由過程ID,現(xiàn)階段命令表針(PC),存儲器結(jié)合和局部變量構(gòu)成。此外,過程是過程里的一個實(shí)體線,被系統(tǒng)軟件多帶帶生產(chǎn)調(diào)度和分配的基本要素,過程自己并不有著服務(wù)器資源。
2.threading.thread()簡單地應(yīng)用
2.1加上過程能是程序執(zhí)行迅速
pythod的thread模塊還是比較最底層的控制模塊,pythod的threading模塊是對thread做了很多包裝,能夠方便快捷被應(yīng)用。
有一點(diǎn)在運(yùn)行時不可缺少的網(wǎng)絡(luò)資源,但是它能與同為1個進(jìn)度的其他線程共享過程所具有的所有網(wǎng)絡(luò)資源。
import threading import time def saySorry(): print("親愛的,我錯了,我能吃飯了嗎?") time.sleep(5) if __name__=="__main__": start_time1=time.time() for i in range(5): t=threading.Thread(target=saySorry) t.start()#啟動線程,即讓線程開始執(zhí)行 end_time1=time.time() print(end_time1-start_time1) start_time2=time.time() for i in range(5): t=saySorry() end_time2=time.time() print(end_time2-start_time2)
輸出為:
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
0.001995086669921875
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
親愛的,我錯了,我能吃飯了嗎?
25.001766204833984
2.2主線程會等待所有的子線程結(jié)束后才結(jié)束
import threading from time import sleep,ctime def sing(): for i in range(3): print("正在唱歌...%d"%i) sleep(1) def dance(): for i in range(3): print("正在跳舞...%d"%i) sleep(1) if __name__=='__main__': print('---開始---:%s'%ctime()) t1=threading.Thread(target=sing) t2=threading.Thread(target=dance) t1.start() t2.start() #sleep(5)#屏蔽此行代碼,試試看,程序是否會立馬結(jié)束? print('---結(jié)束---:%s'%ctime())
輸出為:
---開始---:Mon Sep 28 14:42:09 2020
正在唱歌...0
正在跳舞...0---結(jié)束---:Mon Sep 28 14:42:09 2020
正在唱歌...1
正在跳舞...1
正在唱歌...2
正在跳舞...2
如果釋放‘sleep(5)’,輸出為:
---開始---:Mon Sep 28 14:43:36 2020
正在唱歌...0
正在跳舞...0
正在跳舞...1
正在唱歌...1
正在唱歌...2正在跳舞...2
---結(jié)束---:Mon Sep 28 14:43:41 2020
3.查看線程數(shù)量
import threading from time import sleep,ctime def sing(): for i in range(3): print("正在唱歌...%d"%i) sleep(1) def dance(): for i in range(3): print("正在跳舞...%d"%i) sleep(1) if __name__=='__main__': print('---開始---:%s'%ctime()) t1=threading.Thread(target=sing) t2=threading.Thread(target=dance) t1.start() t2.start() while True: length=len(threading.enumerate()) print('當(dāng)前運(yùn)行的線程數(shù)為:%d'%length) if length<=1: break sleep(0.5)
輸出為:
---開始---:Mon Sep 28 14:46:16 2020
正在唱歌...0
正在跳舞...0
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
正在唱歌...1
正在跳舞...1當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
正在唱歌...2
正在跳舞...2
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:3
當(dāng)前運(yùn)行的線程數(shù)為:1
4.線程參數(shù)及順序
4.1傳遞參數(shù)的方法
使用args傳遞參數(shù)threading.Thread(target=sing,args=(10,100,100))
使用kwargs傳遞參數(shù)threading.Thread(target=sing,kwargs={“a”:10,“b”:100,“c”:100})
同時使用args和kwargs傳遞參數(shù)threading.Thread(target=sing,args=(10,),kwargs={“b”:100,“c”:100})
4.2線程的執(zhí)行順序
import threading import time def sing(): for i in range(5): print("我是sing") time.sleep(1) def dance(): for i in range(5): print("我是dance") time.sleep(1) if __name__=='__main__': #創(chuàng)建兩個子線程 t1=threading.Thread(target=sing) t2=threading.Thread(target=dance) #啟動子線程 t1.start() t2.start()
輸出為:
我是sing
我是dance
我是sing
我是dance
我是dance
我是sing
我是dance我是sing
我是sing
我是dance
說明:
從代碼和執(zhí)行結(jié)果我們可以看出,多線程程序的執(zhí)行順序是不確定的。當(dāng)執(zhí)行到sleep語句時,線程將被阻塞(Blocked),到sleep結(jié)束后,線程進(jìn)入就緒(Runnable)狀態(tài),等待調(diào)度。而線程調(diào)度將自行選擇一個線程執(zhí)行。上面的代碼中只能保證每個線程都運(yùn)行完整個run函數(shù),但是線程的啟動順序、run函數(shù)中每次循環(huán)的執(zhí)行順序都不能確定。
5.守護(hù)線程
守護(hù)線程:如果在程序中將子線程設(shè)置為守護(hù)線程,則該子線程會在主線程結(jié)束時自動退出,設(shè)置方式為thread.setDaemon(True),要在thread.start()之前設(shè)置,默認(rèn)是false的,也就是主線程結(jié)束時,子線程依然在執(zhí)行。
5.1如下代碼,主線程已經(jīng)exit()【其實(shí)并沒有真正結(jié)束】,子線程還在繼續(xù)執(zhí)行
import threading import time def test(): for i in range(7): print("test is run:",i) time.sleep(1) if __name__=='__main__': #創(chuàng)建子線程 t1=threading.Thread(target=test) #啟動子線程 t1.start() #休眠2秒 time.sleep(2) print("我OVER了") #退出 exit()
輸出為:
test is run:0
test is run:1
我OVER了
test is run:2
test is run:3
test is run:4
test is run:5
test is run:6
5.2設(shè)置守護(hù)線程
為線程設(shè)置守護(hù),如果主線程結(jié)束,子線程也隨之結(jié)束。
import threading import time def test(): for i in range(7): print("test is run:",i) time.sleep(1) if __name__=='__main__': #創(chuàng)建子線程 t1=threading.Thread(target=test) #設(shè)置線程保護(hù) t1.setDaemon(True) #啟動子線程 t1.start() #休眠2秒 time.sleep(2) print("我OVER了") #退出 exit() 輸出為: test is run:0 test is run:1 我OVER了 參考代碼 import threading from threading import Lock,Thread import time,os '''
python多線程詳解
什么是線程?
線程也叫輕量級進(jìn)程,是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它被包涵在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。
線程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個進(jìn)程的其他線程共享進(jìn)程所
擁有的全部資源。一個線程可以創(chuàng)建和撤銷另一個線程,同一個進(jìn)程中的多個線程之間可以并發(fā)執(zhí)行
'''
'''
為什么要使用多線程?
線程在程序中是獨(dú)立的、并發(fā)的執(zhí)行流。與分隔的進(jìn)程相比,進(jìn)程中線程之間的隔離程度要小,它們共享內(nèi)存、文件句柄
和其他進(jìn)程應(yīng)有的狀態(tài)。
因?yàn)榫€程的劃分尺度小于進(jìn)程,使得多線程程序的并發(fā)性高。進(jìn)程在執(zhí)行過程之中擁有獨(dú)立的內(nèi)存單元,而多個線程共享
內(nèi)存,從而極大的提升了程序的運(yùn)行效率。
線程比進(jìn)程具有更高的性能,這是由于同一個進(jìn)程中的線程都有共性,多個線程共享一個進(jìn)程的虛擬空間。線程的共享環(huán)境
包括進(jìn)程代碼段、進(jìn)程的共有數(shù)據(jù)等,利用這些共享的數(shù)據(jù),線程之間很容易實(shí)現(xiàn)通信。
操作系統(tǒng)在創(chuàng)建進(jìn)程時,必須為改進(jìn)程分配獨(dú)立的內(nèi)存空間,并分配大量的相關(guān)資源,但創(chuàng)建線程則簡單得多。因此,使用多線程
來實(shí)現(xiàn)并發(fā)比使用多進(jìn)程的性能高得要多。
'''
'''
總結(jié)起來,使用多線程編程具有如下幾個優(yōu)點(diǎn):
進(jìn)程之間不能共享內(nèi)存,但線程之間共享內(nèi)存非常容易。
操作系統(tǒng)在創(chuàng)建進(jìn)程時,需要為該進(jìn)程重新分配系統(tǒng)資源,但創(chuàng)建線程的代價則小得多。因此使用多線程來實(shí)現(xiàn)多任務(wù)并發(fā)執(zhí)行比使用多進(jìn)程的效率高
python語言內(nèi)置了多線程功能支持,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式,從而簡化了python的多線程編程。
''' ''' 普通創(chuàng)建方式 ''' #def run(n): #print('task',n) #time.sleep(1) #print('2s') #time.sleep(1) #print('1s') #time.sleep(1) #print('0s') #time.sleep(1) # #if __name__=='__main__': #t1=threading.Thread(target=run,args=('t1',))#target是要執(zhí)行的函數(shù)名(不是函數(shù)),args是函數(shù)對應(yīng)的參數(shù),以元組的形式存在 #t2=threading.Thread(target=run,args=('t2',)) #t1.start() #t2.start() ''' 自定義線程:繼承threading.Thread來定義線程類,其本質(zhì)是重構(gòu)Thread類中的run方法 ''' #class MyThread(threading.Thread): #def __init__(self,n): #super(MyThread,self).__init__()#重構(gòu)run函數(shù)必須寫 #self.n=n # #def run(self): #print('task',self.n) #time.sleep(1) #print('2s') #time.sleep(1) #print('1s') #time.sleep(1) #print('0s') #time.sleep(1) # #if __name__=='__main__': #t1=MyThread('t1') #t2=MyThread('t2') #t1.start() #t2.start() '''
守護(hù)線程
下面這個例子,這里使用setDaemon(True)把所有的子線程都變成了主線程的守護(hù)線程,
因此當(dāng)主線程結(jié)束后,子線程也會隨之結(jié)束,所以當(dāng)主線程結(jié)束后,整個程序就退出了。
所謂'線程守護(hù)',就是主線程不管該線程的執(zhí)行情況,只要是其他子線程結(jié)束且主線程執(zhí)行完畢,主線程都會關(guān)閉。也就是說:主線程不等待該守護(hù)線程的執(zhí)行完再去關(guān)閉。
''' #def run(n): #print('task',n) #time.sleep(1) #print('3s') #time.sleep(1) #print('2s') #time.sleep(1) #print('1s') # #if __name__=='__main__': #t=threading.Thread(target=run,args=('t1',)) #t.setDaemon(True) #t.start() #print('end') ''' 通過執(zhí)行結(jié)果可以看出,設(shè)置守護(hù)線程之后,當(dāng)主線程結(jié)束時,子線程也將立即結(jié)束,不再執(zhí)行 ''' ''' 主線程等待子線程結(jié)束 為了讓守護(hù)線程執(zhí)行結(jié)束之后,主線程再結(jié)束,我們可以使用join方法,讓主線程等待子線程執(zhí)行 ''' #def run(n): #print('task',n) #time.sleep(2) #print('5s') #time.sleep(2) #print('3s') #time.sleep(2) #print('1s') #if __name__=='__main__': #t=threading.Thread(target=run,args=('t1',)) #t.setDaemon(True)#把子線程設(shè)置為守護(hù)線程,必須在start()之前設(shè)置 #t.start() #t.join()#設(shè)置主線程等待子線程結(jié)束 #print('end') ''' 多線程共享全局變量 線程時進(jìn)程的執(zhí)行單元,進(jìn)程時系統(tǒng)分配資源的最小執(zhí)行單位,所以在同一個進(jìn)程中的多線程是共享資源的 ''' #g_num=100 #def work1(): #global g_num #for i in range(3): #g_num+=1 #print('in work1 g_num is:%d'%g_num) # #def work2(): #global g_num #print('in work2 g_num is:%d'%g_num) # #if __name__=='__main__': #t1=threading.Thread(target=work1) #t1.start() #time.sleep(1) #t2=threading.Thread(target=work2) #t2.start() ''' 由于線程之間是進(jìn)行隨機(jī)調(diào)度,并且每個線程可能只執(zhí)行n條執(zhí)行之后,當(dāng)多個線程同時修改同一條數(shù)據(jù)時可能會出現(xiàn)臟數(shù)據(jù), 所以出現(xiàn)了線程鎖,即同一時刻允許一個線程執(zhí)行操作。線程鎖用于鎖定資源,可以定義多個鎖,像下面的代碼,當(dāng)需要獨(dú)占 某一個資源時,任何一個鎖都可以鎖定這個資源,就好比你用不同的鎖都可以把這個相同的門鎖住一樣。 由于線程之間是進(jìn)行隨機(jī)調(diào)度的,如果有多個線程同時操作一個對象,如果沒有很好地保護(hù)該對象,會造成程序結(jié)果的不可預(yù)期, 我們因此也稱為“線程不安全”。 為了防止上面情況的發(fā)生,就出現(xiàn)了互斥鎖(Lock) ''' #def work(): #global n #lock.acquire() #temp=n #time.sleep(0.1) #n=temp-1 #lock.release() # # #if __name__=='__main__': #lock=Lock() #n=100 #l=[] #for i in range(100): #p=Thread(target=work) #l.append(p) #p.start() #for p in l: #p.join() ''' 遞歸鎖:RLcok類的用法和Lock類一模一樣,但它支持嵌套,在多個鎖沒有釋放的時候一般會使用RLock類 ''' #def func(lock): #global gl_num #lock.acquire() #gl_num+=1 #time.sleep(1) #print(gl_num) #lock.release() # # #if __name__=='__main__': #gl_num=0 #lock=threading.RLock() #for i in range(10): #t=threading.Thread(target=func,args=(lock,)) #t.start() ''' 信號量(BoundedSemaphore類) 互斥鎖同時只允許一個線程更改數(shù)據(jù),而Semaphore是同時允許一定數(shù)量的線程更改數(shù)據(jù),比如廁所有3個坑, 那最多只允許3個人上廁所,后面的人只能等里面有人出來了才能再進(jìn)去 ''' #def run(n,semaphore): #semaphore.acquire()#加鎖 #time.sleep(3) #print('run the thread:%sn'%n) #semaphore.release()#釋放 # # #if __name__=='__main__': #num=0 #semaphore=threading.BoundedSemaphore(5)#最多允許5個線程同時運(yùn)行 #for i in range(22): #t=threading.Thread(target=run,args=('t-%s'%i,semaphore)) #t.start() #while threading.active_count()!=1: #pass #else: #print('----------all threads done-----------') ''' python線程的事件用于主線程控制其他線程的執(zhí)行,事件是一個簡單的線程同步對象,其主要提供以下的幾個方法: clear將flag設(shè)置為False set將flag設(shè)置為True is_set判斷是否設(shè)置了flag wait會一直監(jiān)聽flag,如果沒有檢測到flag就一直處于阻塞狀態(tài) 事件處理的機(jī)制:全局定義了一個Flag,當(dāng)Flag的值為False,那么event.wait()就會阻塞,當(dāng)flag值為True, 那么event.wait()便不再阻塞 ''' event=threading.Event() def lighter(): count=0 event.set()#初始者為綠燈 while True: if 5<count<=10: event.clear()#紅燈,清除標(biāo)志位 print("33[41;lmred light is on...33[0m]") elif count>10: event.set()#綠燈,設(shè)置標(biāo)志位 count=0 else: print('33[42;lmgreen light is on...33[0m') time.sleep(1) count+=1 def car(name): while True: if event.is_set():#判斷是否設(shè)置了標(biāo)志位 print('[%s]running.....'%name) time.sleep(1) else: print('[%s]sees red light,waiting...'%name) event.wait() print('[%s]green light is on,start going...'%name) #startTime=time.time() light=threading.Thread(target=lighter,) light.start() car=threading.Thread(target=car,args=('MINT',)) car.start() endTime=time.time() #print('用時:',endTime-startTime) '''
GIL全局解釋器
在非python環(huán)境中,單核情況下,同時只能有一個任務(wù)執(zhí)行。多核時可以支持多個線程同時執(zhí)行。但是在python中,無論有多少個核
同時只能執(zhí)行一個線程。究其原因,這就是由于GIL的存在導(dǎo)致的。
GIL的全程是全局解釋器,來源是python設(shè)計(jì)之初的考慮,為了數(shù)據(jù)安全所做的決定。某個線程想要執(zhí)行,必須先拿到GIL,我們可以
把GIL看做是“通行證”,并且在一個python進(jìn)程之中,GIL只有一個。拿不到線程的通行證,并且在一個python進(jìn)程中,GIL只有一個,
拿不到通行證的線程,就不允許進(jìn)入CPU執(zhí)行。GIL只在cpython中才有,因?yàn)閏python調(diào)用的是c語言的原生線程,所以他不能直接操
作cpu,而只能利用GIL保證同一時間只能有一個線程拿到數(shù)據(jù)。而在pypy和jpython中是沒有GIL的
python在使用多線程的時候,調(diào)用的是c語言的原生過程。
'''
'''
python針對不同類型的代碼執(zhí)行效率也是不同的
1、CPU密集型代碼(各種循環(huán)處理、計(jì)算等),在這種情況下,由于計(jì)算工作多,ticks技術(shù)很快就會達(dá)到閥值,然后出發(fā)GIL的
釋放與再競爭(多個線程來回切換當(dāng)然是需要消耗資源的),所以python下的多線程對CPU密集型代碼并不友好。
2、IO密集型代碼(文件處理、網(wǎng)絡(luò)爬蟲等設(shè)計(jì)文件讀寫操作),多線程能夠有效提升效率(單線程下有IO操作會進(jìn)行IO等待,
造成不必要的時間浪費(fèi),而開啟多線程能在線程A等待時,自動切換到線程B,可以不浪費(fèi)CPU的資源,從而能提升程序的執(zhí)行
效率)。所以python的多線程對IO密集型代碼比較友好。
'''
'''
主要要看任務(wù)的類型,我們把任務(wù)分為I/O密集型和計(jì)算密集型,而多線程在切換中又分為I/O切換和時間切換。如果任務(wù)屬于是I/O密集型,
若不采用多線程,我們在進(jìn)行I/O操作時,勢必要等待前面一個I/O任務(wù)完成后面的I/O任務(wù)才能進(jìn)行,在這個等待的過程中,CPU處于等待
狀態(tài),這時如果采用多線程的話,剛好可以切換到進(jìn)行另一個I/O任務(wù)。這樣就剛好可以充分利用CPU避免CPU處于閑置狀態(tài),提高效率。但是
如果多線程任務(wù)都是計(jì)算型,CPU會一直在進(jìn)行工作,直到一定的時間后采取多線程時間切換的方式進(jìn)行切換線程,此時CPU一直處于工作狀態(tài),
此種情況下并不能提高性能,相反在切換多線程任務(wù)時,可能還會造成時間和資源的浪費(fèi),導(dǎo)致效能下降。這就是造成上面兩種多線程結(jié)果不能的解釋。
結(jié)論:I/O密集型任務(wù),建議采取多線程,還可以采用多進(jìn)程+協(xié)程的方式(例如:爬蟲多采用多線程處理爬取的數(shù)據(jù));對于計(jì)算密集型任務(wù),python此時就不適用了。
'''
綜上所述,這篇文章就給大家介紹到這里了,希望可以給大家?guī)韼椭?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/128697.html
本文給大家介紹一類從視頻里獲取視頻幀的辦法,因?yàn)閱魏双@取視頻幀速率比較慢,因而接下來我們增強(qiáng)了線程同步的辦法,感興趣的朋友能夠出手試一試 Python迅速獲取視頻幀(線程同步) 現(xiàn)在詳細(xì)介紹一下一類從視頻里獲取視頻幀的辦法,因?yàn)閱魏双@取視頻幀速率比較慢,因而接下來我們增強(qiáng)了線程同步的辦法。 1、獲取視頻幀 獲取視頻幀關(guān)鍵用了Opencv控制模塊。 在其中: camera=cv2.V...
摘要:中關(guān)于線程的標(biāo)準(zhǔn)庫是,之前在版本中的在之后更名為,無論是還是都應(yīng)該盡量避免使用較為底層的而應(yīng)該使用。而與線程相比,協(xié)程尤其是結(jié)合事件循環(huán)無論在編程模型還是語法上,看起來都是非常友好的單線程同步過程。 項(xiàng)目地址:https://git.io/pytips 要說到線程(Thread)與協(xié)程(Coroutine)似乎總是需要從并行(Parallelism)與并發(fā)(Concurrency)談起...
摘要:進(jìn)程可創(chuàng)建多個線程來執(zhí)行同一程序的不同部分。就緒等待線程調(diào)度。運(yùn)行線程正常運(yùn)行阻塞暫停運(yùn)行,解除阻塞后進(jìn)入狀態(tài)重新等待調(diào)度。消亡線程方法執(zhí)行完畢返回或者異常終止。多線程多的情況下,依次執(zhí)行各線程的方法,前頭一個結(jié)束了才能執(zhí)行后面一個。 淺談Python多線程 作者簡介: 姓名:黃志成(小黃)博客: 博客 線程 一.什么是線程? 操作系統(tǒng)原理相關(guān)的書,基本都會提到一句很經(jīng)典的話: 進(jìn)程...
摘要:多線程的理解多進(jìn)程和多線程都可以執(zhí)行多個任務(wù),線程是進(jìn)程的一部分。多線程創(chuàng)建在中,同樣可以實(shí)現(xiàn)多線程,有兩個標(biāo)準(zhǔn)模塊和,不過我們主要使用更高級的模塊。多線程的應(yīng)用場景。 1、多線程的理解 多進(jìn)程和多線程都可以執(zhí)行多個任務(wù),線程是進(jìn)程的一部分。線程的特點(diǎn)是線程之間可以共享內(nèi)存和變量,資源消耗少(不過在Unix環(huán)境中,多進(jìn)程和多線程資源調(diào)度消耗差距不明顯,Unix調(diào)度較快),缺點(diǎn)是線程之間...
摘要:多線程和鎖作者博客進(jìn)程和線程進(jìn)程是執(zhí)行中的計(jì)算機(jī)程序。線程包括開始執(zhí)行順序和結(jié)束三部分。的多進(jìn)程相關(guān)模塊模塊是高級別的多線程模塊。線程鎖當(dāng)多線程爭奪鎖時,允許第一個獲得鎖的線程進(jìn)入臨街區(qū),并執(zhí)行代碼。 Python 多線程和鎖 作者博客:http://zzir.cn/ 進(jìn)程和線程 進(jìn)程是執(zhí)行中的計(jì)算機(jī)程序。每個進(jìn)程都擁有自己的地址空間、內(nèi)存、數(shù)據(jù)棧及其它的輔助數(shù)據(jù)。操作系統(tǒng)管理著所有的...
閱讀 911·2023-01-14 11:38
閱讀 878·2023-01-14 11:04
閱讀 740·2023-01-14 10:48
閱讀 1982·2023-01-14 10:34
閱讀 942·2023-01-14 10:24
閱讀 819·2023-01-14 10:18
閱讀 499·2023-01-14 10:09
閱讀 572·2023-01-14 10:02