<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一篇文章帶你全面解析不一樣的線程

          共 5899字,需瀏覽 12分鐘

           ·

          2021-05-17 07:35

          點擊上方“Python爬蟲與數(shù)據(jù)挖掘”,進(jìn)行關(guān)注

          回復(fù)“書籍”即可獲贈Python從入門到進(jìn)階共10本電子書

          置酒長安道,同心與我違。

          前言

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


          線程

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

          有人說python中的線程是個雞肋,這是因為有了GIL,但是又不是一味的雞肋,畢竟在執(zhí)行io操作時還是挺管用的,只是在執(zhí)行計算時就顯得不盡人意。下面我們來看下線程的具體使用方法:

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

          import threading as t

          2.線程的用法

          tt=t.Thread(group=None,target=None,name=None,args=(),kwargs={},name='',daemon=None)group:線程組,必須是Nonetarget:運行的函數(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)識符。只有在調(diào)用了start()方法之后該屬性才有效tt.join() :逐個執(zhí)行每個線程,執(zhí)行完畢后繼續(xù)往下執(zhí)行tt.run() :自動執(zhí)行線程對象
          t的方法也有:t.active_count(): 返回正在運行線程的數(shù)量t.enumerate(): 返回正在運行線程的列表t.current_thread().getName() 獲取當(dāng)前線程的名字t.TIMEOUT_MAX 設(shè)置t的全局超時時間

          下面我們來看下吧:


          3.創(chuàng)建線程

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

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

          1.單線程
          def xc():    for y in range(100):        print('運行中'+str(y))tt=t.Thread(target=xc,args=()) #方法加入到線程tt.start()  #開始線程tt.join() #等待子線程結(jié)束
          2.多線程
          def xc(num):    print('運行:'+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('運行中'+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('運行:'+str(num))x=Xc()for y in range(10):    x.run(y) #運行

          4.線程鎖

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

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

          一、Lock

          使用方法:

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

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

          n=10lock=t.Lock()def xc(num):    lock.acquire()    print('運行+:'+str(num+n))    print('運行-:'+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在一個線程中多次使用同一資源會造成死鎖。

          死鎖問題:

          n=10lock1=t.Lock()lock2=t.Lock()def xc(num):  lock1.acquire()  print('運行+:'+str(num+n))  lock2.acquire()  print('運行-:'+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()


          二、RLock

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

          使用遞歸鎖來解決死鎖:

          n=10lock1=t.RLock()lock2=t.RLock()def xc(num):  lock1.acquire()  print('運行+:'+str(num+n))  lock2.acquire()  print('運行-:'+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()

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

          #with上下文管理,鎖對象支持上下文管理with lock:   #with表示自動打開自動釋放鎖  for i in range(10): #鎖定期間,其他人不可以干活    print(i)  #上面的和下面的是等價的if lock.acquire(1):#鎖住成功繼續(xù)干活,沒有鎖住成功就一直等待,1代表獨占  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è)置超時時間
          Condition.notify(num)喚醒至多指定數(shù)目個數(shù)的等待的線程,沒有等待的線程就沒有任何操作
          Condition.notify_all() 喚醒所有等待的線程 或者notifyAll()
          def ww(c):  with c:    print('init')    c.wait(timeout=5) #設(shè)置等待超時時間5    print('end')def xx(c):  with c:    print('nono')    c.notifyAll() #喚醒所有線程    print('start')    c.notify(1) #喚醒一個線程    print('21')c=t.Condition() #創(chuàng)建條件t.Thread(target=ww,args=(c,)).start()t.Thread(target=xx,args=(c,)).start()

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


          5.信號量

          信號量可以分為有界信號量和無解信號量,下面我們來具體看看他們的用法:

          一、有界信號量

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

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

          可以看到了多了個release后報錯了。


          二、無界信號量

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

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


          6.Event

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

          event.set()      flag設(shè)置為Trueevent.clear()  flag設(shè)置為Falseevent.is_set()  flag是否為True,如果 event.isSet()==False將阻塞線程;設(shè)置等待flag為True的時長,None為無限等待。等到返回True,未等到超時則返回Falseevent.wait(timeout=None)

          下面通過一個例子具體講述:

          import timee=t.Event()def ff(num):  while True:    if num<5:      e.clear()   #清空信號標(biāo)志      print('清空')    if num>=5:      e.wait(timeout=1) #等待信號標(biāo)志為真      e.set()      print('啟動')      if e.isSet(): #如果信號標(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

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

          l=t.local()  #創(chuàng)建一個線程局部變量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è)為全局變量嗎?我們來看下:

          可以看出他報錯了,產(chǎn)生錯誤的原因是因為這個類中沒有屬性x,我們可以簡單的理解為局部變量就只接受局部。


          8.Timer

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

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

          下面我們來看下:

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

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


          總結(jié)

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

          ------------------- End -------------------

          往期精彩文章推薦:

          歡迎大家點贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持

          想加入Python學(xué)習(xí)群請在后臺回復(fù)【入群

          萬水千山總是情,點個【在看】行不行

          /今日留言主題/

          隨便說一兩句吧~~

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费看黄色片国产馆 | 国产福利网 | 中文字幕在线一区观看 | www.日韩美av | 蜜桃 入口|