日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
一篇文章帶你全面解析不一樣的線程

 [[399456]]

本文轉(zhuǎn)載自微信公眾號(hào)「Python爬蟲與數(shù)據(jù)挖掘」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Python爬蟲與數(shù)據(jù)挖掘公眾號(hào)。

前言

在將今天的知識(shí)點(diǎn)之前,大家是否了解線程,進(jìn)程和協(xié)程了,那我們先來(lái)初步了解下吧。

線程

中央處理器的調(diào)度單元,簡(jiǎn)單點(diǎn)說(shuō)就是程序中的末端執(zhí)行者,相當(dāng)于小弟的位置。

有人說(shuō)python中的線程是個(gè)雞肋,這是因?yàn)橛辛薌IL,但是又不是一味的雞肋,畢竟在執(zhí)行io操作時(shí)還是挺管用的,只是在執(zhí)行計(jì)算時(shí)就顯得不盡人意。下面我們來(lái)看下線程的具體使用方法:

1.導(dǎo)入線程模塊:

 
 
 
 
  1. import threading as t 

2.線程的用法

 
 
 
 
  1. tt=t.Thread(group=None,target=None,name=None,args=(),kwargs={},name='',daemon=None) 
  2. group:線程組,必須是None 
  3. target:運(yùn)行的函數(shù) 
  4. args:傳入函數(shù)的參數(shù)元組 
  5. kwargs:傳入函數(shù)的參數(shù)字典 
  6. name:線程名 
  7. daemon:線程是否隨主線程退出而退出(守護(hù)線程) 
  8.  
  9. Thread方法的返回值還有以下方法: 
  10. tt.start() : 激活線程, 
  11. tt.getName() : 獲取線程的名稱 
  12. tt.setName() :設(shè)置線程的名稱  
  13. tt.name : 獲取或設(shè)置線程的名稱 
  14. tt.is_alive() :判斷線程是否為激活狀態(tài) 
  15. tt.isAlive() :判斷線程是否為激活狀態(tài) 
  16. tt.setDaemon() 設(shè)置為守護(hù)線程(默認(rèn):False) 
  17. tt.isDaemon() :判斷是否為守護(hù)線程 
  18. tt.ident :獲取線程的標(biāo)識(shí)符。只有在調(diào)用了start()方法之后該屬性才有效 
  19. tt.join() :逐個(gè)執(zhí)行每個(gè)線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行 
  20. tt.run() :自動(dòng)執(zhí)行線程對(duì)象 
  21.  
  22. t的方法也有: 
  23. t.active_count(): 返回正在運(yùn)行線程的數(shù)量 
  24. t.enumerate(): 返回正在運(yùn)行線程的列表 
  25. t.current_thread().getName() 獲取當(dāng)前線程的名字 
  26. t.TIMEOUT_MAX 設(shè)置t的全局超時(shí)時(shí)間 

下面我們來(lái)看下吧:

3.創(chuàng)建線程

線程可以使用Thread方法創(chuàng)建,也可以重寫線程類的run方法實(shí)現(xiàn),線程可分為單線程和多線程。

一、使用Thread方法來(lái)創(chuàng)建:

1.單線程

 
 
 
 
  1. def xc(): 
  2.     for y in range(100): 
  3.         print('運(yùn)行中'+str(y)) 
  4. tt=t.Thread(target=xc,args=()) #方法加入到線程 
  5. tt.start()  #開始線程 
  6. tt.join() #等待子線程結(jié)束 

2.多線程

 
 
 
 
  1. def xc(num): 
  2.     print('運(yùn)行:'+str(num)) 
  3. c=[] 
  4. for y in range(100): 
  5.     tt=t.Thread(target=xc,args=(y,)) 
  6.     tt.start() #開始線程 
  7.     c.append(tt) #創(chuàng)建列表并添加線程 
  8. for x in c: 
  9.     x.join()  #等待子線程結(jié)束 

二、重寫線程的類方法

1.單線程

 
 
 
 
  1. class Xc(t.Thread): #繼承Thread類 
  2.     def __init__(self): 
  3.         super(Xc, self).__init__()  
  4.     def run(self):  #重寫run方法 
  5.         for y in range(100): 
  6.             print('運(yùn)行中'+str(y)) 
  7. x=Xc()  
  8. x.start() #開始線程 
  9. x.join()  #等待子線程結(jié)束 
  10.  
  11. 也可以這么寫: 
  12. Xc().run() 和上面的效果是一樣的 

2.多線程

 
 
 
 
  1. class Xc(t.Thread): #繼承Thread類 
  2.     def __init__(self): 
  3.         super(Xc, self).__init__()  
  4.     def run(self,num):  #重寫run方法 
  5.         print('運(yùn)行:'+str(num)) 
  6. x=Xc() 
  7. for y in range(10): 
  8.     x.run(y) #運(yùn)行 

4.線程鎖

為什么要加鎖,看了這個(gè)你就知道了:

多線程在運(yùn)行時(shí)同時(shí)訪問(wèn)一個(gè)對(duì)象會(huì)產(chǎn)生搶占資源的情況,所以我們必須得束縛它,所以就要給他加一把鎖把他鎖住,這就是同步鎖。要了解鎖,我們得先創(chuàng)建鎖,線程中有兩種鎖:Lock和RLock。

一、Lock

使用方法:

 
 
 
 
  1. # 獲取鎖 
  2. 當(dāng)獲取不到鎖時(shí),默認(rèn)進(jìn)入阻塞狀態(tài),設(shè)置超時(shí)時(shí)間,直到獲取到鎖,后才繼續(xù)。非阻塞時(shí),timeout禁止設(shè)置。如果超時(shí)依舊未獲取到鎖,返回False。 
  3. Lock.acquire(blocking=True,timeout=1)    
  4.  
  5. #釋放鎖,已上鎖的鎖,會(huì)被設(shè)置為unlocked。如果未上鎖調(diào)用,會(huì)拋出RuntimeError異常。 
  6. Lock.release() 

互斥鎖,同步數(shù)據(jù),解決多線程的安全問(wèn)題:

 
 
 
 
  1. n=10 
  2. lock=t.Lock() 
  3. def xc(num): 
  4.     lock.acquire() 
  5.     print('運(yùn)行+:'+str(num+n)) 
  6.     print('運(yùn)行-:'+str(num-n)) 
  7.     lock.release() 
  8. c=[] 
  9. for y in range(10): 
  10.     tt=t.Thread(target=xc,args=(y,)) 
  11.     tt.start() 
  12.     c.append(tt) 
  13. for x in c: 
  14.     x.join() 

這樣就顯得有條理了,而且輸出也是先+后-。Lock在一個(gè)線程中多次使用同一資源會(huì)造成死鎖。

死鎖問(wèn)題:

 
 
 
 
  1. n=10 
  2. lock1=t.Lock() 
  3. lock2=t.Lock() 
  4. def xc(num): 
  5.   lock1.acquire() 
  6.   print('運(yùn)行+:'+str(num+n)) 
  7.   lock2.acquire() 
  8.   print('運(yùn)行-:'+str(num-n)) 
  9.   lock2.release() 
  10.   lock1.release() 
  11. c=[] 
  12. for y in range(10): 
  13.   tt=t.Thread(target=xc,args=(y,)) 
  14.   tt.start() 
  15.   c.append(tt) 
  16. for x in c: 
  17.   x.join() 

二、RLock

相比Lock它可以遞歸,支持在同一線程中多次請(qǐng)求同一資源,并允許在同一線程中被多次鎖定,但是acquire和release必須成對(duì)出現(xiàn)。

使用遞歸鎖來(lái)解決死鎖:

 
 
 
 
  1. n=10 
  2. lock1=t.RLock() 
  3. lock2=t.RLock() 
  4. def xc(num): 
  5.   lock1.acquire() 
  6.   print('運(yùn)行+:'+str(num+n)) 
  7.   lock2.acquire() 
  8.   print('運(yùn)行-:'+str(num-n)) 
  9.   lock2.release() 
  10.   lock1.release() 
  11. c=[] 
  12. for y in range(10): 
  13.   tt=t.Thread(target=xc,args=(y,)) 
  14.   tt.start() 
  15.   c.append(tt) 
  16. for x in c: 
  17.   x.join() 

這時(shí)候,輸出變量就變得僅僅有條了,不在隨意搶占資源。關(guān)于線程鎖,還可以使用with更加方便:

 
 
 
 
  1. #with上下文管理,鎖對(duì)象支持上下文管理 
  2. with lock:   #with表示自動(dòng)打開自動(dòng)釋放鎖 
  3.   for i in range(10): #鎖定期間,其他人不可以干活 
  4.     print(i) 
  5.   #上面的和下面的是等價(jià)的 
  6. if lock.acquire(1):#鎖住成功繼續(xù)干活,沒有鎖住成功就一直等待,1代表獨(dú)占 
  7.   for i in range(10): #鎖定期間,其他線程不可以干活 
  8.     print(i) 
  9.   lock.release() #釋放鎖 

三、條件鎖

等待通過(guò),Condition(lock=None),可以傳入lock或者Rlock,默認(rèn)Rlock,使用方法:

 
 
 
 
  1. Condition.acquire(*args)      獲取鎖 
  2.  
  3. Condition.wait(timeout=None)  等待通知,timeout設(shè)置超時(shí)時(shí)間 
  4.  
  5. Condition.notify(num)喚醒至多指定數(shù)目個(gè)數(shù)的等待的線程,沒有等待的線程就沒有任何操作 
  6.  
  7. Condition.notify_all()  喚醒所有等待的線程 或者notifyAll() 
 
 
 
 
  1. def ww(c): 
  2.   with c: 
  3.     print('init') 
  4.     c.wait(timeout=5) #設(shè)置等待超時(shí)時(shí)間5 
  5.     print('end') 
  6. def xx(c): 
  7.   with c: 
  8.     print('nono') 
  9.     c.notifyAll() #喚醒所有線程 
  10.     print('start') 
  11.     c.notify(1) #喚醒一個(gè)線程 
  12.     print('21') 
  13. c=t.Condition() #創(chuàng)建條件 
  14. t.Thread(target=ww,args=(c,)).start() 
  15. t.Thread(target=xx,args=(c,)).start() 

這樣就可以在等待的時(shí)候喚醒函數(shù)里喚醒其他函數(shù)里所存在的其他線程了。

5.信號(hào)量

信號(hào)量可以分為有界信號(hào)量和無(wú)解信號(hào)量,下面我們來(lái)具體看看他們的用法:

一、有界信號(hào)量

它不允許使用release超出初始值的范圍,否則,拋出ValueError異常。

 
 
 
 
  1. #構(gòu)造方法。value為初始信號(hào)量。value小于0,拋出ValueError異常 
  2. b=t.BoundedSemaphore(value=1)   
  3.  
  4. #獲取信號(hào)量時(shí),計(jì)數(shù)器減1,即_value的值減少1。如果_value的值為0會(huì)變成阻塞狀態(tài)。獲取成功返回True 
  5. BoundedSemaphore.acquire(blocking=True,timeout=None)   
  6.  
  7. #釋放信號(hào)量,計(jì)數(shù)器加1。即_value的值加1,超過(guò)初始化值會(huì)拋出異常ValueError。 
  8. BoundedSemaphore.release()   
  9.  
  10. #信號(hào)量,當(dāng)前信號(hào)量 
  11. BoundedSemaphore._value 

可以看到了多了個(gè)release后報(bào)錯(cuò)了。

二、無(wú)界信號(hào)量

它不檢查release的上限情況,只是單純的加減計(jì)數(shù)器。

可以看到雖然多了個(gè)release,但是沒有問(wèn)題,而且信號(hào)量的數(shù)量不受限制。

6.Event

線程間通信,通過(guò)線程設(shè)置的信號(hào)標(biāo)志(flag)的False 還是True來(lái)進(jìn)行操作,常見方法有:

 
 
 
 
  1. event.set()      flag設(shè)置為True 
  2. event.clear()  flag設(shè)置為False 
  3. event.is_set()  flag是否為True,如果 event.isSet()==False將阻塞線程; 
  4. 設(shè)置等待flag為True的時(shí)長(zhǎng),None為無(wú)限等待。等到返回True,未等到超時(shí)則返回False 
  5. event.wait(timeout=None) 

下面通過(guò)一個(gè)例子具體講述:

 
 
 
 
  1. import time 
  2. e=t.Event() 
  3. def ff(num): 
  4.   while True: 
  5.     if num<5: 
  6.       e.clear()   #清空信號(hào)標(biāo)志 
  7.       print('清空') 
  8.     if num>=5: 
  9.       e.wait(timeout=1) #等待信號(hào)標(biāo)志為真 
  10.       e.set() 
  11.       print('啟動(dòng)') 
  12.       if e.isSet(): #如果信號(hào)標(biāo)志為真則清除標(biāo)志 
  13.         e.clear() 
  14.         print('停止') 
  15.     if num==10: 
  16.       e.wait(timeout=3) 
  17.       e.clear() 
  18.       print('退出') 
  19.       break 
  20.     num+=1 
  21.     time.sleep(2) 
  22. ff(1) 

設(shè)置延遲后可以看到效果相當(dāng)明顯,我們讓他干什么事他就干什么事。

7.local

可以為各個(gè)線程創(chuàng)建完全屬于它們自己的變量(線程局部變量),而且它們的值都在當(dāng)前調(diào)用它的線程當(dāng)中,以字典的形式存在。下面我們來(lái)看下:

 
 
 
 
  1. l=t.local()  #創(chuàng)建一個(gè)線程局部變量 
  2. def ff(num): 
  3.   l.x=100  #設(shè)置l變量的x方法的值為100 
  4.   for y in range(num): 
  5.     l.x+=3 #改變值 
  6.   print(str(l.x)) 
  7.  
  8. for y in range(10): 
  9.   t.Thread(target=ff,args=(y,)).start() #開始執(zhí)行線程 

那么,可以將變量的x方法設(shè)為全局變量嗎?我們來(lái)看下:

可以看出他報(bào)錯(cuò)了,產(chǎn)生錯(cuò)誤的原因是因?yàn)檫@個(gè)類中沒有屬性x,我們可以簡(jiǎn)單的理解為局部變量就只接受局部。

8.Timer

設(shè)置定時(shí)計(jì)劃,可以在規(guī)定的時(shí)間內(nèi)反復(fù)執(zhí)行某個(gè)方法。他的使用方法是:

 
 
 
 
  1. t.Timer(num,func,*args,**kwargs) #在指定時(shí)間內(nèi)再次重啟程序 

下面我們來(lái)看下:

 
 
 
 
  1. def f(): 
  2.   print('start') 
  3.   global t #防止造成線程堆積導(dǎo)致最終程序退出 
  4.   tt= t.Timer(3, f) 
  5.   tt.start() 
  6. f() 

這樣就達(dá)到了每三秒執(zhí)行一次f函數(shù)的效果。

總結(jié)

通過(guò)對(duì)線程的全面解析我們了解到了線程的重要性,它可以將我們復(fù)雜的問(wèn)題變得簡(jiǎn)單化,對(duì)于喜歡玩爬蟲的小伙伴們可以說(shuō)是相當(dāng)有用了,本文基本覆蓋了線程的所有概念,希望能幫到大家。


網(wǎng)站標(biāo)題:一篇文章帶你全面解析不一樣的線程
文章位置:http://m.5511xx.com/article/ccepdhe.html