web開發(fā)中的線程是怎樣的,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)服務(wù)商,為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)服務(wù),網(wǎng)站設(shè)計(jì),網(wǎng)站托管運(yùn)營等一站式綜合服務(wù)型公司,專業(yè)打造企業(yè)形象網(wǎng)站,讓您在眾多競爭對(duì)手中脫穎而出成都創(chuàng)新互聯(lián)。
中央處理器的調(diào)度單元,簡單點(diǎn)說就是程序中的末端執(zhí)行者,相當(dāng)于小弟的位置。
有人說python中的線程是個(gè)雞肋,這是因?yàn)橛辛薌IL,但是又不是一味的雞肋,畢竟在執(zhí)行io操作時(shí)還是挺管用的,只是在執(zhí)行計(jì)算時(shí)就顯得不盡人意。下面我們來看下線程的具體使用方法:
1.導(dǎo)入線程模塊:
import threading as t
2.線程的用法
tt=t.Thread(group=None,target=None,name=None,args=(),kwargs={},name='',daemon=None) group:線程組,必須是None target:運(yùn)行的函數(shù) args:傳入函數(shù)的參數(shù)元組 kwargs:傳入函數(shù)的參數(shù)字典 name:線程名 daemon:線程是否隨主線程退出而退出(守護(hù)線程) Thread方法的返回值還有以下方法: tt.start() : 激活線程, tt.getName() : 獲取線程的名稱 tt.setName() :設(shè)置線程的名稱 tt.name : 獲取或設(shè)置線程的名稱 tt.is_alive() :判斷線程是否為激活狀態(tài) tt.isAlive() :判斷線程是否為激活狀態(tài) tt.setDaemon() 設(shè)置為守護(hù)線程(默認(rèn):False) tt.isDaemon() :判斷是否為守護(hù)線程 tt.ident :獲取線程的標(biāo)識(shí)符。只有在調(diào)用了start()方法之后該屬性才有效 tt.join() :逐個(gè)執(zhí)行每個(gè)線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行 tt.run() :自動(dòng)執(zhí)行線程對(duì)象 t的方法也有: t.active_count(): 返回正在運(yùn)行線程的數(shù)量 t.enumerate(): 返回正在運(yùn)行線程的列表 t.current_thread().getName() 獲取當(dāng)前線程的名字 t.TIMEOUT_MAX 設(shè)置t的全局超時(shí)時(shí)間
下面我們來看下吧:
3.創(chuàng)建線程
線程可以使用Thread方法創(chuàng)建,也可以重寫線程類的run方法實(shí)現(xiàn),線程可分為單線程和多線程。
1.單線程
def xc(): for y in range(100): print('運(yùn)行中'+str(y)) tt=t.Thread(target=xc,args=()) #方法加入到線程 tt.start() #開始線程 tt.join() #等待子線程結(jié)束
2.多線程
def xc(num): print('運(yùn)行:'+str(num)) c=[] for y in range(100): tt=t.Thread(target=xc,args=(y,)) tt.start() #開始線程 c.append(tt) #創(chuàng)建列表并添加線程 for x in c: x.join() #等待子線程結(jié)束
1.單線程
class Xc(t.Thread): #繼承Thread類 def __init__(self): super(Xc, self).__init__() def run(self): #重寫run方法 for y in range(100): print('運(yùn)行中'+str(y)) x=Xc() x.start() #開始線程 x.join() #等待子線程結(jié)束 也可以這么寫: Xc().run() 和上面的效果是一樣的
2.多線程
class Xc(t.Thread): #繼承Thread類 def __init__(self): super(Xc, self).__init__() def run(self,num): #重寫run方法 print('運(yùn)行:'+str(num)) x=Xc() for y in range(10): x.run(y) #運(yùn)行
4.線程鎖
為什么要加鎖,看了這個(gè)你就知道了:
多線程在運(yùn)行時(shí)同時(shí)訪問一個(gè)對(duì)象會(huì)產(chǎn)生搶占資源的情況,所以我們必須得束縛它,所以就要給他加一把鎖把他鎖住,這就是同步鎖。要了解鎖,我們得先創(chuàng)建鎖,線程中有兩種鎖:Lock和RLock。
使用方法:
# 獲取鎖 當(dāng)獲取不到鎖時(shí),默認(rèn)進(jìn)入阻塞狀態(tài),設(shè)置超時(shí)時(shí)間,直到獲取到鎖,后才繼續(xù)。非阻塞時(shí),timeout禁止設(shè)置。如果超時(shí)依舊未獲取到鎖,返回False。 Lock.acquire(blocking=True,timeout=1) #釋放鎖,已上鎖的鎖,會(huì)被設(shè)置為unlocked。如果未上鎖調(diào)用,會(huì)拋出RuntimeError異常。 Lock.release()
互斥鎖,同步數(shù)據(jù),解決多線程的安全問題:
n=10 lock=t.Lock() def xc(num): lock.acquire() print('運(yùn)行+:'+str(num+n)) print('運(yùn)行-:'+str(num-n)) lock.release() c=[] for y in range(10): tt=t.Thread(target=xc,args=(y,)) tt.start() c.append(tt) for x in c: x.join()
這樣就顯得有條理了,而且輸出也是先+后-。Lock在一個(gè)線程中多次使用同一資源會(huì)造成死鎖。
死鎖問題:
n=10 lock1=t.Lock() lock2=t.Lock() def xc(num): lock1.acquire() print('運(yùn)行+:'+str(num+n)) lock2.acquire() print('運(yùn)行-:'+str(num-n)) lock2.release() lock1.release() c=[] for y in range(10): tt=t.Thread(target=xc,args=(y,)) tt.start() c.append(tt) for x in c: x.join()
相比Lock它可以遞歸,支持在同一線程中多次請(qǐng)求同一資源,并允許在同一線程中被多次鎖定,但是acquire和release必須成對(duì)出現(xiàn)。
使用遞歸鎖來解決死鎖:
n=10 lock1=t.RLock() lock2=t.RLock() def xc(num): lock1.acquire() print('運(yùn)行+:'+str(num+n)) lock2.acquire() print('運(yùn)行-:'+str(num-n)) lock2.release() lock1.release() c=[] for y in range(10): tt=t.Thread(target=xc,args=(y,)) tt.start() c.append(tt) for x in c: x.join()
這時(shí)候,輸出變量就變得僅僅有條了,不在隨意搶占資源。關(guān)于線程鎖,還可以使用with更加方便:
#with上下文管理,鎖對(duì)象支持上下文管理 with lock: #with表示自動(dòng)打開自動(dòng)釋放鎖 for i in range(10): #鎖定期間,其他人不可以干活 print(i) #上面的和下面的是等價(jià)的 if lock.acquire(1):#鎖住成功繼續(xù)干活,沒有鎖住成功就一直等待,1代表獨(dú)占 for i in range(10): #鎖定期間,其他線程不可以干活 print(i) lock.release() #釋放鎖
等待通過,Condition(lock=None),可以傳入lock或者Rlock,默認(rèn)Rlock,使用方法:
Condition.acquire(*args) 獲取鎖 Condition.wait(timeout=None) 等待通知,timeout設(shè)置超時(shí)時(shí)間 Condition.notify(num)喚醒至多指定數(shù)目個(gè)數(shù)的等待的線程,沒有等待的線程就沒有任何操作 Condition.notify_all() 喚醒所有等待的線程 或者notifyAll()
def ww(c): with c: print('init') c.wait(timeout=5) #設(shè)置等待超時(shí)時(shí)間5 print('end') def xx(c): with c: print('nono') c.notifyAll() #喚醒所有線程 print('start') c.notify(1) #喚醒一個(gè)線程 print('21') c=t.Condition() #創(chuàng)建條件 t.Thread(target=ww,args=(c,)).start() t.Thread(target=xx,args=(c,)).start()
這樣就可以在等待的時(shí)候喚醒函數(shù)里喚醒其他函數(shù)里所存在的其他線程了。
5.信號(hào)量
信號(hào)量可以分為有界信號(hào)量和無解信號(hào)量,下面我們來具體看看他們的用法:
它不允許使用release超出初始值的范圍,否則,拋出ValueError異常。
#構(gòu)造方法。value為初始信號(hào)量。value小于0,拋出ValueError異常 b=t.BoundedSemaphore(value=1) #獲取信號(hào)量時(shí),計(jì)數(shù)器減1,即_value的值減少1。如果_value的值為0會(huì)變成阻塞狀態(tài)。獲取成功返回True BoundedSemaphore.acquire(blocking=True,timeout=None) #釋放信號(hào)量,計(jì)數(shù)器加1。即_value的值加1,超過初始化值會(huì)拋出異常ValueError。 BoundedSemaphore.release() #信號(hào)量,當(dāng)前信號(hào)量 BoundedSemaphore._value
可以看到了多了個(gè)release后報(bào)錯(cuò)了。
它不檢查release的上限情況,只是單純的加減計(jì)數(shù)器。
可以看到雖然多了個(gè)release,但是沒有問題,而且信號(hào)量的數(shù)量不受限制。
6.Event
線程間通信,通過線程設(shè)置的信號(hào)標(biāo)志(flag)的False 還是True來進(jìn)行操作,常見方法有:
event.set() flag設(shè)置為True event.clear() flag設(shè)置為False event.is_set() flag是否為True,如果 event.isSet()==False將阻塞線程; 設(shè)置等待flag為True的時(shí)長,None為無限等待。等到返回True,未等到超時(shí)則返回False event.wait(timeout=None)
下面通過一個(gè)例子具體講述:
import time e=t.Event() def ff(num): while True: if num<5: e.clear() #清空信號(hào)標(biāo)志 print('清空') if num>=5: e.wait(timeout=1) #等待信號(hào)標(biāo)志為真 e.set() print('啟動(dòng)') if e.isSet(): #如果信號(hào)標(biāo)志為真則清除標(biāo)志 e.clear() print('停止') if num==10: e.wait(timeout=3) e.clear() print('退出') break num+=1 time.sleep(2) ff(1)
設(shè)置延遲后可以看到效果相當(dāng)明顯,我們讓他干什么事他就干什么事。
7.local
可以為各個(gè)線程創(chuàng)建完全屬于它們自己的變量(線程局部變量),而且它們的值都在當(dāng)前調(diào)用它的線程當(dāng)中,以字典的形式存在。下面我們來看下:
l=t.local() #創(chuàng)建一個(gè)線程局部變量 def ff(num): l.x=100 #設(shè)置l變量的x方法的值為100 for y in range(num): l.x+=3 #改變值 print(str(l.x)) for y in range(10): t.Thread(target=ff,args=(y,)).start() #開始執(zhí)行線程
那么,可以將變量的x方法設(shè)為全局變量嗎?我們來看下:
可以看出他報(bào)錯(cuò)了,產(chǎn)生錯(cuò)誤的原因是因?yàn)檫@個(gè)類中沒有屬性x,我們可以簡單的理解為局部變量就只接受局部。
8.Timer
設(shè)置定時(shí)計(jì)劃,可以在規(guī)定的時(shí)間內(nèi)反復(fù)執(zhí)行某個(gè)方法。他的使用方法是:
t.Timer(num,func,*args,**kwargs) #在指定時(shí)間內(nèi)再次重啟程序
下面我們來看下:
def f(): print('start') global t #防止造成線程堆積導(dǎo)致最終程序退出 tt= t.Timer(3, f) tt.start() f()
這樣就達(dá)到了每三秒執(zhí)行一次f函數(shù)的效果。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。