python多線程詳解

      網友投稿 809 2025-04-01

      一、多線程介紹

      1、什么是線程?

      線程也叫輕量級進程,是操作系統能夠進行運算調度的最小單位,它被包涵在進程之中,是進程中的實際運作單位。

      線程自己不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其他線程共享進程所擁有的全部資源。

      python多線程詳解

      一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以并發執行。

      2、為什么要使用多線程?

      線程在程序中是獨立的、并發的執行流。與分隔的進程相比,進程中線程之間的隔離程度要小,它們共享內存、文件句柄和其他進程應有的狀態。

      因為線程的劃分尺度小于進程,使得多線程程序的并發性高。進程在執行過程之中擁有獨立的內存單元,而多個線程共享內存,從而極大的提升了程序的運行效率。

      線程比進程具有更高的性能,這是由于同一個進程中的線程都有共性,多個線程共享一個進程的虛擬空間。線程的共享環境 包括進程代碼段、進程的共有數據等,利用這些共享的數據,線程之間很容易實現通信。

      操作系統在創建進程時,必須為改進程分配獨立的內存空間,并分配大量的相關資源,但創建線程則簡單得多。因此,使用多線程來實現并發比使用多進程的性能高得要多。

      3、總結起來,使用多線程編程具有如下幾個優點:

      進程之間不能共享內存,但線程之間共享內存非常容易。

      操作系統在創建進程時,需要為該進程重新分配系統資源,但創建線程的代價則小得多。因此使用多線程來實現多任務并發執行比使用多進程的效率高。

      Python語言內置了多線程功能支持,而不是單純地作為底層操作系統的調度方式,從而簡化了Python的多線程編程。

      二、代碼舉例

      import threading

      import time,os

      '''

      1、普通創建方式

      '''

      # 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__':

      # target是要執行的函數名(不是函數),args是函數對應的參數,以元組的形式存在

      # ? ? t1 = threading.Thread(target=run,args=('t1',))

      # ? ? t2 = threading.Thread(target=run,args=('t2',))

      # ? ? t1.start()

      # ? ? t2.start()

      '''

      2、自定義線程:繼承threading.Thread來定義線程類,

      其本質是重構Thread類中的run方法

      '''

      # class MyThread(threading.Thread):

      # ? ? def __init__(self,n):

      # ? ? ? ? super(MyThread,self).__init__() ? #重構run函數必須寫

      # ? ? ? ? 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()

      '''

      3、守護線程

      下面這個例子,這里使用setDaemon(True)把所有的子線程都變成了主線程的守護線程,

      因此當主線程結束后,子線程也會隨之結束,所以當主線程結束后,整個程序就退出了。

      所謂’線程守護’,就是主線程不管守護線程的執行情況,只要是其他非守護子線程結束且主線程執行完畢,

      主線程都會關閉。也就是說:主線程不等待守護線程的執行完再去關閉。

      主線程在其他非守護線程運行完畢后才算運行完畢(守護線程在此時就被回收)。

      因為主線程的結束意味著進程的結束,進程整體的資源都將被回收,

      而進程必須保證非守護線程都運行完畢后才能結束。

      '''

      # 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')

      '''

      通過執行結果可以看出,設置守護線程之后,當主線程結束時,子線程也將立即結束,不再執行

      '''

      '''

      4、主線程等待子線程結束

      為了讓守護線程執行結束之后,主線程再結束,我們可以使用join方法,讓主線程等待守護線程執行完畢再結束。

      '''

      # 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) ? ?#把子線程設置為守護線程,必須在start()之前設置

      # ? ? t.start()

      # ? ? t.join() ? ? #設置主線程等待子線程結束

      # ? ? print('end')

      '''

      5、多線程共享全局變量

      線程是進程的執行單元,進程是系統分配資源的最小執行單位,所以在同一個進程中的多線程是共享資源的。

      '''

      # 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()

      '''

      6、互斥鎖(Lock)

      由于線程之間是進行隨機調度,當多個線程同時修改同一條數據時可能會出現臟數據,

      所以出現了線程鎖,即同一時刻只允許一個線程執行某些操作。

      線程鎖用于鎖定資源,可以定義多個鎖,像下面的代碼,當需要獨占某一個資源時,

      任何一個鎖都可以鎖定這個資源,就好比你用不同的鎖都可以把這個相同的門鎖住一樣。

      由于線程之間是進行隨機調度的,如果有多個線程同時操作一個對象,

      如果沒有很好地保護該對象,會造成程序結果的不可預期,也稱為“線程不安全”。

      為了防止上面情況的發生,就出現了互斥鎖(Lock)

      '''

      # def work():

      # ? ? global n

      # ? ? lock.acquire()

      # ? ? temp = n

      # ? ? time.sleep(0.1)

      # ? ? n = temp-1

      # ? ? lock.release()

      #

      #

      # if __name__ == '__main__':

      # ? ? lock = threading.Lock()

      # ? ? n = 100

      # ? ? l = []

      # ? ? for i in range(100):

      # ? ? ? ? p = Thread(target=work)

      # ? ? ? ? l.append(p)

      # ? ? ? ? p.start()

      # ? ? for p in l:

      # ? ? ? ? p.join()

      '''

      7、遞歸鎖:RLcok類的用法和Lock類一模一樣,但它支持嵌套。

      RLock類代表可重入鎖(Reentrant Lock)。

      對于可重入鎖,在同一個線程中可以對它進行多次鎖定,

      也可以多次釋放。如果使用 RLock,那么 acquire() 和 release() 方法必須成對出現。

      如果調用了 n 次 acquire() 加鎖,則必須調用 n 次 release() 才能釋放鎖。

      由此可見,RLock 鎖具有可重入性。也就是說,同一個線程可以對已被加鎖的 RLock 鎖再次加鎖,

      RLock 對象會維持一個計數器來追蹤 acquire() 方法的嵌套調用,

      線程在每次調用 acquire() 加鎖后,都必須顯式調用 release() 方法來釋放鎖。

      所以,一段被鎖保護的方法可以調用另一個被相同鎖保護的方法。

      '''

      # 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()

      '''

      8、信號量(BoundedSemaphore類)

      互斥鎖同時只允許一個線程更改數據,而Semaphore是同時允許一定數量的線程更改數據,

      比如廁所有3個坑,那最多只允許3個人上廁所,后面的人只能等里面有人出來了才能再進去

      '''

      # def run(n,semaphore):

      # ? ? semaphore.acquire() ? #加鎖

      # ? ? time.sleep(3)

      # ? ? print('run the thread:%s\n' % n)

      # ? ? semaphore.release() ? ?#釋放

      #

      #

      # if __name__== '__main__':

      # ? ? num=0

      # ? ? semaphore = threading.BoundedSemaphore(5) ? #最多允許5個線程同時運行

      # ? ? 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-----------')

      '''

      9、python線程事件

      用于主線程控制其他線程的執行,事件是一個簡單的線程同步對象,其主要提供以下的幾個方法:

      clear將flag設置為 False

      set將flag設置為 True

      is_set判斷是否設置了flag

      wait會一直監聽flag,如果沒有檢測到flag就一直處于阻塞狀態

      事件處理的機制:全局定義了一個Flag,

      當Flag的值為False,那么event.wait()就會阻塞,

      當flag值為True,那么event.wait()便不再阻塞

      '''

      event = threading.Event()

      def lighter():

      count = 0

      event.set() ? ? ? ? #初始者為綠燈

      while True:

      if 5 < count <=10:

      event.clear() ?#紅燈,清除標志位

      print("\33[41;lmred light is on...\033[0m]")

      elif count > 10:

      event.set() ? ?#綠燈,設置標志位

      count = 0

      else:

      print('\33[42;lmgreen light is on...\033[0m')

      time.sleep(1)

      count += 1

      def car(name):

      while True:

      if event.is_set(): ? ? #判斷是否設置了標志位

      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環境中,單核情況下,同時只能有一個任務執行。

      多核時可以支持多個線程同時執行。

      但是在python中,無論有多少個核同時只能執行一個線程。

      究其原因,這就是由于GIL的存在導致的。

      GIL的全程是全局解釋器,來源是python設計之初的考慮,為了數據安全所做的決定。

      某個線程想要執行,必須先拿到GIL,我們可以

      把GIL看做是“通行證”,并且在一個python進程之中,GIL只有一個。

      拿不到線程的通行證,并且在一個python進程中,GIL只有一個,

      拿不到通行證的線程,就不允許進入CPU執行。

      GIL只在cpython中才有,因為cpython調用的是c語言的原生線程,

      所以他不能直接操作cpu,而只能利用GIL保證同一時間只能有一個線程拿到數據。

      而在pypy和jpython中是沒有GIL的python在使用多線程的時候,調用的是c語言的原生過程。

      '''

      '''

      python針對不同類型的代碼執行效率也是不同的

      1、CPU密集型代碼(各種循環處理、計算等),在這種情況下,由于計算工作多,

      ticks技術很快就會達到閥值,然后出發GIL的釋放與再競爭

      (多個線程來回切換當然是需要消耗資源的),

      所以python下的多線程對CPU密集型代碼并不友好。

      2、IO密集型代碼(文件處理、網絡爬蟲等設計文件讀寫操作),

      多線程能夠有效提升效率(單線程下有IO操作會進行IO等待,

      造成不必要的時間浪費,而開啟多線程能在線程A等待時,自動切換到線程B,

      可以不浪費CPU的資源,從而能提升程序的執行效率)。

      所以python的多線程對IO密集型代碼比較友好。

      '''

      '''

      主要要看任務的類型,我們把任務分為I/O密集型和計算密集型,

      而多線程在切換中又分為I/O切換和時間切換。

      如果任務屬于是I/O密集型,

      若不采用多線程,我們在進行I/O操作時,勢必要等待前面一個I/O任務完成后面的I/O任務才能進行,

      在這個等待的過程中,CPU處于等待狀態,這時如果采用多線程的話,

      剛好可以切換到進行另一個I/O任務。這樣就剛好可以充分利用CPU避免CPU處于閑置狀態,提高效率。

      但是如果多線程任務都是計算型,CPU會一直在進行工作,

      直到一定的時間后采取多線程時間切換的方式進行切換線程,此時CPU一直處于工作狀態,

      此種情況下并不能提高性能,相反在切換多線程任務時,可能還會造成時間和資源的浪費,

      導致效能下降。這就是造成上面兩種多線程結果不能的解釋。

      結論:I/O密集型任務,建議采取多線程,還可以采用多進程+協程的方式

      (例如:爬蟲多采用多線程處理爬取的數據);

      對于計算密集型任務,python此時就不適用了。

      '''

      Python 任務調度 多線程

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:工作流程管理系統(工作流管理讓資源使用更合理)
      下一篇:制造型生產企業管理流程圖(生產制造型企業的業務流程圖)
      相關文章
      亚洲国产精品成人精品无码区 | 亚洲精品在线播放| 亚洲五月综合缴情在线观看| 亚洲精品国产V片在线观看 | 91亚洲va在线天线va天堂va国产 | 亚洲AV中文无码字幕色三| 国产成人综合亚洲AV第一页 | 亚洲av无码一区二区三区观看| 亚洲男人天堂2017| 亚洲最新视频在线观看| 日韩精品亚洲人成在线观看| 亚洲一二成人精品区| 亚洲资源在线视频| 亚洲国产精品人久久电影| 亚洲婷婷综合色高清在线| 亚洲一卡二卡三卡四卡无卡麻豆| 亚洲伊人久久大香线蕉影院| 亚洲xxxxxx| 亚洲人成人网站18禁| 含羞草国产亚洲精品岁国产精品 | 亚洲日本一线产区和二线产区对比| 亚洲综合精品伊人久久| 亚洲风情亚Aⅴ在线发布| 精品韩国亚洲av无码不卡区| 五月婷婷亚洲综合| 国产亚洲午夜高清国产拍精品 | 久久亚洲av无码精品浪潮| 亚洲乱码无码永久不卡在线| 亚洲AV综合色区无码一区爱AV| 99久久亚洲精品无码毛片| 亚洲午夜一区二区电影院| 亚洲一线产区二线产区精华| 亚洲色成人网站WWW永久四虎| 色综合久久精品亚洲国产| 亚洲欧洲国产成人综合在线观看 | 中文文字幕文字幕亚洲色| 亚洲精品久久久久无码AV片软件| 在线亚洲精品视频| 亚洲午夜国产精品无码老牛影视 | 亚洲A∨午夜成人片精品网站| 中文字幕亚洲天堂|