python多線(xiàn)程超詳細(xì)詳解
python中的多線(xiàn)程是一個(gè)非常重要的知識(shí)點(diǎn),今天為大家對(duì)多線(xiàn)程進(jìn)行詳細(xì)的說(shuō)明,代碼中的注釋有多線(xiàn)程的知識(shí)點(diǎn)還有測(cè)試用的實(shí)例。
import threading
from threading import Lock,Thread
import time,os
'''
python多線(xiàn)程詳解
什么是線(xiàn)程?
線(xiàn)程也叫輕量級(jí)進(jìn)程,是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它被包涵在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。
線(xiàn)程自己不擁有系統(tǒng)資源,只擁有一點(diǎn)兒在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其他線(xiàn)程共享進(jìn)程所
擁有的全部資源。一個(gè)線(xiàn)程可以創(chuàng)建和撤銷(xiāo)另一個(gè)線(xiàn)程,同一個(gè)進(jìn)程中的多個(gè)線(xiàn)程之間可以并發(fā)執(zhí)行
'''
'''
為什么要使用多線(xiàn)程?
線(xiàn)程在程序中是獨(dú)立的、并發(fā)的執(zhí)行流。與分隔的進(jìn)程相比,進(jìn)程中線(xiàn)程之間的隔離程度要小,它們共享內(nèi)存、文件句柄
和其他進(jìn)程應(yīng)有的狀態(tài)。
因?yàn)榫€(xiàn)程的劃分尺度小于進(jìn)程,使得多線(xiàn)程程序的并發(fā)性高。進(jìn)程在執(zhí)行過(guò)程之中擁有獨(dú)立的內(nèi)存單元,而多個(gè)線(xiàn)程共享
內(nèi)存,從而極大的提升了程序的運(yùn)行效率。
線(xiàn)程比進(jìn)程具有更高的性能,這是由于同一個(gè)進(jìn)程中的線(xiàn)程都有共性,多個(gè)線(xiàn)程共享一個(gè)進(jìn)程的虛擬空間。線(xiàn)程的共享環(huán)境
包括進(jìn)程代碼段、進(jìn)程的共有數(shù)據(jù)等,利用這些共享的數(shù)據(jù),線(xiàn)程之間很容易實(shí)現(xiàn)通信。
操作系統(tǒng)在創(chuàng)建進(jìn)程時(shí),必須為改進(jìn)程分配獨(dú)立的內(nèi)存空間,并分配大量的相關(guān)資源,但創(chuàng)建線(xiàn)程則簡(jiǎn)單得多。因此,使用多線(xiàn)程
來(lái)實(shí)現(xiàn)并發(fā)比使用多進(jìn)程的性能高得要多。
'''
'''
總結(jié)起來(lái),使用多線(xiàn)程編程具有如下幾個(gè)優(yōu)點(diǎn):
進(jìn)程之間不能共享內(nèi)存,但線(xiàn)程之間共享內(nèi)存非常容易。
操作系統(tǒng)在創(chuàng)建進(jìn)程時(shí),需要為該進(jìn)程重新分配系統(tǒng)資源,但創(chuàng)建線(xiàn)程的代價(jià)則小得多。因此使用多線(xiàn)程來(lái)實(shí)現(xiàn)多任務(wù)并發(fā)執(zhí)行比使用多進(jìn)程的效率高
python語(yǔ)言?xún)?nèi)置了多線(xiàn)程功能支持,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式,從而簡(jiǎn)化了python的多線(xiàn)程編程。
'''
'''
普通創(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ù)對(duì)應(yīng)的參數(shù),以元組的形式存在
# t2 = threading.Thread(target=run,args=('t2',))
# t1.start()
# t2.start()
'''
自定義線(xiàn)程:繼承threading.Thread來(lái)定義線(xiàn)程類(lèi),其本質(zhì)是重構(gòu)Thread類(lèi)中的run方法
'''
# class MyThread(threading.Thread):
# def __init__(self,n):
# super(MyThread,self).__init__() #重構(gòu)run函數(shù)必須寫(xiě)
# 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ù)線(xiàn)程
下面這個(gè)例子,這里使用setDaemon(True)把所有的子線(xiàn)程都變成了主線(xiàn)程的守護(hù)線(xiàn)程,
因此當(dāng)主線(xiàn)程結(jié)束后,子線(xiàn)程也會(huì)隨之結(jié)束,所以當(dāng)主線(xiàn)程結(jié)束后,整個(gè)程序就退出了。
所謂'線(xiàn)程守護(hù)',就是主線(xiàn)程不管該線(xiàn)程的執(zhí)行情況,只要是其他子線(xiàn)程結(jié)束且主線(xiàn)程執(zhí)行完畢,主線(xiàn)程都會(huì)關(guān)閉。也就是說(shuō):主線(xiàn)程不等待該守護(hù)線(xiàn)程的執(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')
'''
通過(guò)執(zhí)行結(jié)果可以看出,設(shè)置守護(hù)線(xiàn)程之后,當(dāng)主線(xiàn)程結(jié)束時(shí),子線(xiàn)程也將立即結(jié)束,不再執(zhí)行
'''
'''
主線(xiàn)程等待子線(xiàn)程結(jié)束
為了讓守護(hù)線(xiàn)程執(zhí)行結(jié)束之后,主線(xiàn)程再結(jié)束,我們可以使用join方法,讓主線(xiàn)程等待子線(xiàn)程執(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) #把子線(xiàn)程設(shè)置為守護(hù)線(xiàn)程,必須在start()之前設(shè)置
# t.start()
# t.join() #設(shè)置主線(xiàn)程等待子線(xiàn)程結(jié)束
# print('end')
'''
多線(xiàn)程共享全局變量
線(xiàn)程時(shí)進(jìn)程的執(zhí)行單元,進(jìn)程時(shí)系統(tǒng)分配資源的最小執(zhí)行單位,所以在同一個(gè)進(jìn)程中的多線(xià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()
'''
由于線(xiàn)程之間是進(jìn)行隨機(jī)調(diào)度,并且每個(gè)線(xiàn)程可能只執(zhí)行n條執(zhí)行之后,當(dāng)多個(gè)線(xiàn)程同時(shí)修改同一條數(shù)據(jù)時(shí)可能會(huì)出現(xiàn)臟數(shù)據(jù),
所以出現(xiàn)了線(xiàn)程鎖,即同一時(shí)刻允許一個(gè)線(xiàn)程執(zhí)行操作。線(xiàn)程鎖用于鎖定資源,可以定義多個(gè)鎖,像下面的代碼,當(dāng)需要獨(dú)占
某一個(gè)資源時(shí),任何一個(gè)鎖都可以鎖定這個(gè)資源,就好比你用不同的鎖都可以把這個(gè)相同的門(mén)鎖住一樣。
由于線(xiàn)程之間是進(jìn)行隨機(jī)調(diào)度的,如果有多個(gè)線(xiàn)程同時(shí)操作一個(gè)對(duì)象,如果沒(méi)有很好地保護(hù)該對(duì)象,會(huì)造成程序結(jié)果的不可預(yù)期,
我們因此也稱(chēng)為“線(xiàn)程不安全”。
為了防止上面情況的發(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類(lèi)的用法和Lock類(lèi)一模一樣,但它支持嵌套,在多個(gè)鎖沒(méi)有釋放的時(shí)候一般會(huì)使用RLock類(lèi)
'''
# 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()
'''
信號(hào)量(BoundedSemaphore類(lèi))
互斥鎖同時(shí)只允許一個(gè)線(xiàn)程更改數(shù)據(jù),而Semaphore是同時(shí)允許一定數(shù)量的線(xiàn)程更改數(shù)據(jù),比如廁所有3個(gè)坑,
那最多只允許3個(gè)人上廁所,后面的人只能等里面有人出來(lái)了才能再進(jìn)去
'''
# 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個(gè)線(xiàn)程同時(shí)運(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線(xiàn)程的事件用于主線(xiàn)程控制其他線(xiàn)程的執(zhí)行,事件是一個(gè)簡(jiǎn)單的線(xiàn)程同步對(duì)象,其主要提供以下的幾個(gè)方法:
clear將flag設(shè)置為 False
set將flag設(shè)置為 True
is_set判斷是否設(shè)置了flag
wait會(huì)一直監(jiān)聽(tīng)flag,如果沒(méi)有檢測(cè)到flag就一直處于阻塞狀態(tài)
事件處理的機(jī)制:全局定義了一個(gè)Flag,當(dāng)Flag的值為False,那么event.wait()就會(huì)阻塞,當(dāng)flag值為T(mén)rue,
那么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...\033[0m]")
elif count > 10:
event.set() #綠燈,設(shè)置標(biāo)志位
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(): #判斷是否設(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('用時(shí):',endTime-startTime)
'''
GIL 全局解釋器
在非python環(huán)境中,單核情況下,同時(shí)只能有一個(gè)任務(wù)執(zhí)行。多核時(shí)可以支持多個(gè)線(xiàn)程同時(shí)執(zhí)行。但是在python中,無(wú)論有多少個(gè)核
同時(shí)只能執(zhí)行一個(gè)線(xiàn)程。究其原因,這就是由于GIL的存在導(dǎo)致的。
GIL的全程是全局解釋器,來(lái)源是python設(shè)計(jì)之初的考慮,為了數(shù)據(jù)安全所做的決定。某個(gè)線(xiàn)程想要執(zhí)行,必須先拿到GIL,我們可以
把GIL看做是“通行證”,并且在一個(gè)python進(jìn)程之中,GIL只有一個(gè)。拿不到線(xiàn)程的通行證,并且在一個(gè)python進(jìn)程中,GIL只有一個(gè),
拿不到通行證的線(xiàn)程,就不允許進(jìn)入CPU執(zhí)行。GIL只在cpython中才有,因?yàn)閏python調(diào)用的是c語(yǔ)言的原生線(xiàn)程,所以他不能直接操
作cpu,而只能利用GIL保證同一時(shí)間只能有一個(gè)線(xiàn)程拿到數(shù)據(jù)。而在pypy和jpython中是沒(méi)有GIL的
python在使用多線(xiàn)程的時(shí)候,調(diào)用的是c語(yǔ)言的原生過(guò)程。
'''
'''
python針對(duì)不同類(lèi)型的代碼執(zhí)行效率也是不同的
1、CPU密集型代碼(各種循環(huán)處理、計(jì)算等),在這種情況下,由于計(jì)算工作多,ticks技術(shù)很快就會(huì)達(dá)到閥值,然后出發(fā)GIL的
釋放與再競(jìng)爭(zhēng)(多個(gè)線(xiàn)程來(lái)回切換當(dāng)然是需要消耗資源的),所以python下的多線(xiàn)程對(duì)CPU密集型代碼并不友好。
2、IO密集型代碼(文件處理、網(wǎng)絡(luò)爬蟲(chóng)等設(shè)計(jì)文件讀寫(xiě)操作),多線(xiàn)程能夠有效提升效率(單線(xiàn)程下有IO操作會(huì)進(jìn)行IO等待,
造成不必要的時(shí)間浪費(fèi),而開(kāi)啟多線(xiàn)程能在線(xiàn)程A等待時(shí),自動(dòng)切換到線(xiàn)程B,可以不浪費(fèi)CPU的資源,從而能提升程序的執(zhí)行
效率)。所以python的多線(xiàn)程對(duì)IO密集型代碼比較友好。
'''
'''
主要要看任務(wù)的類(lèi)型,我們把任務(wù)分為I/O密集型和計(jì)算密集型,而多線(xiàn)程在切換中又分為I/O切換和時(shí)間切換。如果任務(wù)屬于是I/O密集型,
若不采用多線(xiàn)程,我們?cè)谶M(jìn)行I/O操作時(shí),勢(shì)必要等待前面一個(gè)I/O任務(wù)完成后面的I/O任務(wù)才能進(jìn)行,在這個(gè)等待的過(guò)程中,CPU處于等待
狀態(tài),這時(shí)如果采用多線(xiàn)程的話(huà),剛好可以切換到進(jìn)行另一個(gè)I/O任務(wù)。這樣就剛好可以充分利用CPU避免CPU處于閑置狀態(tài),提高效率。但是
如果多線(xiàn)程任務(wù)都是計(jì)算型,CPU會(huì)一直在進(jìn)行工作,直到一定的時(shí)間后采取多線(xiàn)程時(shí)間切換的方式進(jìn)行切換線(xiàn)程,此時(shí)CPU一直處于工作狀態(tài),
此種情況下并不能提高性能,相反在切換多線(xiàn)程任務(wù)時(shí),可能還會(huì)造成時(shí)間和資源的浪費(fèi),導(dǎo)致效能下降。這就是造成上面兩種多線(xiàn)程結(jié)果不能的解釋。
結(jié)論:I/O密集型任務(wù),建議采取多線(xiàn)程,還可以采用多進(jìn)程+協(xié)程的方式(例如:爬蟲(chóng)多采用多線(xiàn)程處理爬取的數(shù)據(jù));對(duì)于計(jì)算密集型任務(wù),python此時(shí)就不適用了。
'''
到此這篇關(guān)于python多線(xiàn)程超詳細(xì)詳解的文章就介紹到這了,更多相關(guān)python多線(xiàn)程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python讀取英文文件并記錄每個(gè)單詞出現(xiàn)次數(shù)后降序輸出示例
這篇文章主要介紹了Python讀取英文文件并記錄每個(gè)單詞出現(xiàn)次數(shù)后降序輸出,涉及Python文件讀取、字符串替換、分割以及字典遍歷、排序等相關(guān)操作技巧,需要的朋友可以參考下2018-06-06
解決nohup執(zhí)行python程序log文件寫(xiě)入不及時(shí)的問(wèn)題
今天小編就為大家分享一篇解決nohup執(zhí)行python程序log文件寫(xiě)入不及時(shí)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
python 進(jìn)程 進(jìn)程池 進(jìn)程間通信實(shí)現(xiàn)解析
這篇文章主要介紹了python 進(jìn)程 進(jìn)程池 進(jìn)程間通信實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
Python tabulate結(jié)合loguru打印出美觀(guān)方便的日志記錄
在開(kāi)發(fā)過(guò)程中經(jīng)常碰到在本地環(huán)境無(wú)法完成聯(lián)調(diào)測(cè)試的情況,必須到統(tǒng)一的聯(lián)機(jī)環(huán)境對(duì)接其他系統(tǒng)測(cè)試。往往是出現(xiàn)了BUG難以查找數(shù)據(jù)記錄及時(shí)定位到錯(cuò)誤出現(xiàn)的位置。本文將利用tabulate結(jié)合loguru實(shí)現(xiàn)打印出美觀(guān)方便的日志記錄,需要的可以參考一下2022-10-10
用 Python 定義 Schema 并生成 Parquet 文件詳情
本文將演示兩個(gè)例子,一個(gè)是沒(méi)有層級(jí)的兩個(gè)字段,另一個(gè)是含于嵌套級(jí)別的字段,將要使用到的 Python 模塊有 pandas 和 pyarrow,感興趣是我小伙伴請(qǐng)和小編一起學(xué)習(xí)下面文章內(nèi)容吧2021-09-09
Python依賴(lài)管理及打包工具Poetry使用規(guī)范
這篇文章主要為大家介紹了Python依賴(lài)管理及打包工具Poetry的依賴(lài)規(guī)范,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-09-09

