您好,登錄后才能下訂單哦!
?
目錄
并發(fā)和并行:... 1
parallel,并行:... 1
concurrency,并發(fā):... 2
并發(fā)的解決:... 2
1、隊(duì)列、緩沖區(qū):... 2
2、爭(zhēng)搶:... 3
3、預(yù)處理:... 3
4、并行:... 3
5、提速:... 3
6、消息中間件:... 3
進(jìn)程和線程:... 4
線程的狀態(tài):... 5
py的線程開發(fā):... 5
threading.Thread類:... 6
線程啟動(dòng):... 6
線程退出:... 7
線程的傳參:... 8
threading的屬性和方法:... 8
Thread實(shí)例的屬性和方法:... 9
t.start()和t.run():... 9
線程安全:... 11
daemon和non-daemon:... 11
t.join() 14
threading.local類:... 15
threading.Timer類:... 18
?
?
?
同時(shí)做某些事,可互不干擾的同一時(shí)刻做幾件事;
?
例:
高速公路的車道,所有車輛(數(shù)據(jù))可互不干擾的在自己的車道上奔跑(傳輸);在同一時(shí)刻,每條車道上可能同時(shí)有車輛在跑,是同時(shí)發(fā)生的概念;
?
在一定程度上,并行可解決并發(fā)問題;并行并不是解決并發(fā)的最佳方案;
?
?
也是同時(shí)做某些事,但是強(qiáng)調(diào),同一時(shí)段做了幾件事;
?
例:
鄉(xiāng)村公路一條車道,半幅路面出現(xiàn)了坑,交警指揮車輛走另外半幅路面,一個(gè)方向放行3min,換另一個(gè)方向,發(fā)生了車輛同時(shí)要通過路面的事件,這就是并發(fā);
?
并發(fā)是在一時(shí)間點(diǎn)有很多事要做,并發(fā)太大,如何做?怎么做并不知道;
?
隊(duì)列、緩沖區(qū):
鄉(xiāng)村公路窄,車輛就會(huì)排隊(duì),所以排隊(duì)是一種天然解決并發(fā)的辦法,車輛排隊(duì)的緩沖地帶就是緩沖區(qū);
?
并行:
鄉(xiāng)村公路窄,拓寬成雙向四車道,可同時(shí)雙向車道通行,通行車輛增多,能滿足一定的通行需求;
?
?
食堂打飯模型:
中午12點(diǎn),開飯,都涌向食堂,這就是并發(fā),如果人很多,就是高并發(fā);
?
假設(shè)只有一個(gè)窗口,陸續(xù)涌入食堂的,排隊(duì)打菜是比較好的方式;
排隊(duì)就是把人排成隊(duì)列,先進(jìn)先出,解決了資源使用的問題;
排成的隊(duì)列,其實(shí)就是一個(gè)緩沖地帶,就是緩沖區(qū);
假設(shè)女生優(yōu)先,那么這個(gè)窗口就得是兩隊(duì),只要有女生就先打飯,男生排隊(duì)等著,女生隊(duì)伍就是一個(gè)優(yōu)先隊(duì)列;
?
只開一個(gè)窗口,有可能沒有秩序,也就是誰(shuí)擠進(jìn)去就給誰(shuí)打飯;
擠到窗口的人占據(jù)窗口,直到打到飯菜離開;
其他人繼續(xù)爭(zhēng)搶,會(huì)有一個(gè)人正占據(jù)窗口,可以視為鎖定窗口,窗口就不能為其他人提供了服務(wù)了,這是一種鎖機(jī)制;
搶到資源就上鎖,排他性鎖,其他人只能等候;
爭(zhēng)搶也是一種高并發(fā)解決方案,但不好,因?yàn)橛锌赡苡腥撕荛L(zhǎng)時(shí)間搶不到;
?
如果排長(zhǎng)隊(duì)的原因,是由于每個(gè)人打菜等候時(shí)間長(zhǎng),因?yàn)橐缘牟藳]有,需要現(xiàn)做,沒打著飯不走開,鎖定著窗口;
可以提前統(tǒng)計(jì)大多數(shù)最愛吃的菜品,最愛吃的80%的熱門菜提前做好,保證供應(yīng),20%的冷門菜現(xiàn)做;
這樣大多數(shù)人,就算鎖定窗口,也很快就釋放窗口了;
一種提前加載用戶需要的數(shù)據(jù)的思路,預(yù)處理思想,緩存常用;
?
成百上千人同時(shí)來吃飯,一個(gè)隊(duì)伍搞不定的,多開打飯窗口形成多個(gè)隊(duì)列,如同開多個(gè)車道一樣,并行打菜;
開窗口就得擴(kuò)大食堂,得雇人在每一個(gè)窗口提供服務(wù),造成成本上升;
日??赏ㄟ^購(gòu)買更多服務(wù)器,或多開進(jìn)程實(shí)現(xiàn)并行處理,來解決并發(fā)問題;
這些都是水平擴(kuò)展思想;
?
提高單個(gè)窗口的打飯速度;
打飯人員提高工作技能,或?yàn)閱蝹€(gè)窗口配備更多的服務(wù)人員,都是提速的辦法;
這是一種垂直擴(kuò)展思想;
?
北京地鐵站外九曲回腸走廊,緩沖人流,進(jìn)去之后再多口安檢進(jìn)站;
rabbitmq、activemq(apache)、rocketmq(ali apache)、kafka(apache)等;
?
以上是最常用的解決方案,一般來說不同的并發(fā)場(chǎng)景用不同的策略,而策略可能是多種方式的優(yōu)化組合;
如,多開食堂(多地),可把食堂建設(shè)到宿舍生活區(qū)(就近),技術(shù)來源于生活;
?
注:
如果線程在單cpu上處理,就不是并行了;
一般server都是多cpu的,服務(wù)的部署往往是多機(jī)的、分布式的,這都是并行處理;
真正的好系統(tǒng),是串行、并行共存的;
串行、并行是解決并發(fā)的手段之一,不可厚此薄彼;
串行、并行哪個(gè)快?問題本身有問題,類似1車道快還是4車道快;
?
?
?
在實(shí)現(xiàn)了線程的OS中,線程是OS能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位,一個(gè)程序的執(zhí)行實(shí)例就是一個(gè)進(jìn)程;
?
process,進(jìn)程:
是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是OS結(jié)構(gòu)的基礎(chǔ);
?
進(jìn)程和程序的關(guān)系:
程序是源代碼編譯后的文件,而這些文件存放在disk上,當(dāng)程序被OS加載到內(nèi)存中,就是進(jìn)程,進(jìn)程中存放著指令和數(shù)據(jù)(資源),它也是線程的容器;
?
linux進(jìn)程有父進(jìn)程、子進(jìn)程;
win的進(jìn)程是平等關(guān)系;
?
thread,線程:
有時(shí)被稱為light weight process,LWP,輕量級(jí)進(jìn)程,線程是程序執(zhí)行流的最小單元;
一個(gè)標(biāo)準(zhǔn)的線程由線程ID、當(dāng)前指令指針PC、寄存器集合、堆棧組成;
?
process、thread理解:
現(xiàn)代OS提出進(jìn)程的概念,每一個(gè)進(jìn)程都認(rèn)為自己獨(dú)占所有的計(jì)算機(jī)硬件資源;
進(jìn)程就是獨(dú)立的王國(guó),進(jìn)程間不可以隨便的共享數(shù)據(jù);
進(jìn)程是國(guó),線程是省;
線程就是省份,同一個(gè)進(jìn)程內(nèi)的線程可共享進(jìn)程的資源,每一個(gè)線程擁有自己獨(dú)立的堆棧;
進(jìn)程是容器,進(jìn)程內(nèi)的線程是干活的;
線程是程序執(zhí)行流的最小單元,基本單元是進(jìn)程;
?
py中的進(jìn)程和線程:
進(jìn)程會(huì)啟動(dòng)一個(gè)解釋器進(jìn)程,線程共享一個(gè)解釋器進(jìn)程;
?
?
?
ready,就緒態(tài),線程能夠運(yùn)行,但在等待被調(diào)度,可能線程剛剛創(chuàng)建啟動(dòng),或剛剛從阻塞中恢復(fù),或被其它線程搶占;
running,運(yùn)行態(tài),線程正在運(yùn)行;
blocked,阻塞態(tài),線程等待外部事件發(fā)生而無法運(yùn)行,如IO操作;
terminated,終止,線程完成、或退出、或被取消;
?
?
?
使用標(biāo)準(zhǔn)庫(kù)threading;
?
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
target,線程調(diào)用的對(duì)象,就是目標(biāo)函數(shù);
name,為線程起個(gè)名,可重名,區(qū)分是靠線程ID;
args,為目標(biāo)函數(shù)傳遞實(shí)參,元組;
kwargs,為目標(biāo)函數(shù)傳遞關(guān)鍵字參數(shù),字典;
?
py的線程沒有優(yōu)先級(jí),沒有線程組的概念,也不能被銷毀、停止、掛起,也就沒有恢復(fù)、中斷了;
?
例:
import threading
import time
?
def worker():
? ??for _ in range(5):
??????? time.sleep(1)
??????? print('welcome magedu')
??? print('thread over')
?
def worker1():
??? for _ in range(5):
??????? time.sleep(1)
??????? print('welcome magedu...')
??? print('thread over...')
?
t = threading.Thread(target=worker, name='w1')?? #實(shí)例化一個(gè)線程對(duì)象
t.start()?? #線程啟動(dòng),每個(gè)線程必須且只能執(zhí)行該方法1次
# print('~'*30)
# t.start()?? #RuntimeError: threads can only be started once,t.start()只能一次,如果非要再寫一次必須先實(shí)例化t=threading.Thread(...)再啟動(dòng)t.start()
t1 = threading.Thread(target=worker1, name='w2')?? #對(duì)于1顆cpu的server,多線程是假并行
t1.start()
輸出:
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
welcome magedu...
welcome magedu
thread over
thread over...
?
py沒有提供線程退出的方法,線程在如下情況時(shí)退出:
1、線程函數(shù)內(nèi)語(yǔ)句執(zhí)行完畢;
2、線程函數(shù)中拋出未處理的異常;
例:
import threading
import time
?
def worker2():
??? count = 0
??? while True:?? #通過在循環(huán)中加標(biāo)記,當(dāng)某一變量為指定值時(shí)退出循環(huán)
??????? time.sleep(1)
??????? print('thread quit')
??????? count += 1
??????? if count > 3:
??????????? raise Exception('new exception')
?
t2 = threading.Thread(target=worker2, name='t2')
t2.start()
輸出:
thread quit
thread quit
thread quit
thread quit
Exception in thread t2:
Traceback (most recent call last):
? File "D:\Python\Python35\lib\threading.py", line 914, in _bootstrap_inner
??? self.run()
? File "D:\Python\Python35\lib\threading.py", line 862, in run
??? self._target(*self._args, **self._kwargs)
? File "E:/git_practice/cmdb/example_threading.py", line 23, in worker2
??? raise Exception('new exception')
Exception: new exception
?
本質(zhì)上就是函數(shù)傳參,線程傳參和函數(shù)傳參沒什么區(qū)別;
例:
import threading
import time
?
def worker3(n):
??? for _ in range(n):
??????? print('thread arguments')
?
# t3 = threading.Thread(target=worker3, name='t3', args=(3,))
t3 = threading.Thread(target=worker3, name='t3', kwargs={'n':3})
t3.start()
輸出:
thread arguments
thread arguments
thread arguments
?
current_thread(),返回當(dāng)前線程對(duì)象;
main_thread(),返回主線程對(duì)象;
active_count(),當(dāng)前處于alive狀態(tài)(一旦被cpu調(diào)度才是活動(dòng)狀態(tài)ready<->running)的線程個(gè)數(shù);
enumerate(),返回所有活著的線程的列表,不包括已終止的線程和未開始的線程;
get_ident(),返回當(dāng)前線程的ID,非0整數(shù);
?
例:
def worker(n=3):
??? print('current_thread: {}'.format(threading.current_thread()))
??? print('current_thread().name: {}'.format(threading.current_thread().name))
??? print('currrent_thread().is_alive(): {}'.format(threading.current_thread().is_alive()))
??? print('main_thread: {}'.format(threading.main_thread()))
??? print('main_thread().is_alive(): {}'.format(threading.main_thread().is_alive()))
??? print('active_count: {}'.format(threading.active_count()))
??? print('enumerate: {}'.format(threading.enumerate()))
?
??? for _ in range(n):
??????? time.sleep(0.5)
??????? print('welcome to magedu')
??? print('thread over')
?
print('current_thread again: {}'.format(threading.current_thread()))
print('current_thread().is_alive() again: {}'.format(threading.current_thread().is_alive()))
?
t = threading.Thread(target=worker,name='w1')
t.start()
# t1 = threading.Thread(target=worker,name='w2')?? #多線程無法確定誰(shuí)前誰(shuí)后,每個(gè)都有機(jī)會(huì)被執(zhí)行到
# t1.start()
輸出:
current_thread again: <_MainThread(MainThread, started 18324)>
current_thread().is_alive() again: True
current_thread: <Thread(w1, started 14036)>
current_thread().name: w1
currrent_thread().is_alive(): True
main_thread: <_MainThread(MainThread, started 18324)>
main_thread().is_alive(): True
active_count: 2
enumerate: [<Thread(w1, started 14036)>, <_MainThread(MainThread, started 18324)>]
welcome to magedu
welcome to magedu
welcome to magedu
thread over
?
t.name,只是個(gè)名字,是個(gè)標(biāo)識(shí),可重名,getName()獲取,setName()設(shè)置;
t.ident,線程ID,是非0整數(shù),線程啟動(dòng)后才會(huì)有ID,否則為None;線程退出,此ID依舊可訪問,此ID可重復(fù)使用;t.name可重復(fù),t.ident必須唯一但可在線程退出后再利用;
t.is_alive(),返回線程是否活著,True|False;
?
t.start(),用于多線程場(chǎng)景,一執(zhí)行到t1.start()則開啟一個(gè)新的工作線程,threading.current_thread()為<MyThread(w1, started 17760)>,主線程1個(gè)工作線程多個(gè);
t.run(),運(yùn)行線程函數(shù),僅是普通函數(shù)調(diào)用,不會(huì)開啟新的線程,在當(dāng)前線程中順序執(zhí)行,threading.current_thrad()為<_MainThread(MainThread, started 15384)>,先執(zhí)行t1.run()再執(zhí)行t2.run();
?
注:多線程:
一個(gè)進(jìn)程中有多個(gè)線程,實(shí)現(xiàn)并發(fā);
一個(gè)進(jìn)程至少有一個(gè)主線程,其它線程為工作線程;
一個(gè)進(jìn)程至少有一個(gè)線程,作為程序入口,這個(gè)線程就是主線程;
多線程爭(zhēng)用同一個(gè)資源(打飯時(shí)很多人爭(zhēng)一個(gè)窗口),解決:加鎖;
父線程不管理子線程,父、子只是為了分清誰(shuí)啟動(dòng)的誰(shuí);
父線程消亡,子線程仍可繼續(xù)運(yùn)行;
主線程結(jié)束時(shí),可殺掉工作線程(子線程);
?
例:
class MyThread(threading.Thread):
??? def run(self):
??????? print('run')
??????? super().run()
?
??? def start(self):
??????? print('start')
??????? return super().start()
?
def worker(n=3):
??? print(threading.current_thread())
??? for _ in range(n):
??????? time.sleep(1)
??????? print('welcome to magedu')
??? print('thread over')
?
t1 = MyThread(target=worker,name='w1')
t1.start()
# t1.run()
# t2 = MyThread(target=worker,name='w2')
# t2.start()
# t2.run()
輸出:
start
run
<MyThread(w1, started 18324)>
welcome to magedu
welcome to magedu
welcome to magedu
thread over
?
多線程的執(zhí)行結(jié)果,有時(shí)是意想不到的,即線程不安全,在各種高級(jí)語(yǔ)言中都必須面對(duì);
如下例在pycharm中和ipython中分別執(zhí)行,pycharm中整整齊齊,而ipython中不規(guī)整,ipython中運(yùn)行的效果就是線程不安全;
?
其它語(yǔ)言中有:
線程安全函數(shù);
線程不安全函數(shù)(要加鎖);
線程安全類;
線程不安全類(加各種鎖);
?
在多線程或特殊場(chǎng)景下應(yīng)用,能不加鎖就不加,否則效率極差;
?
例:
def worker():
??? for _ in range(100):
??????? print('{} is running'.format(threading.current_thread()))
?
for _ in range(2):
??? threading.Thread(target=worker,name='worker-{}'.format('view')).start()
解決:
def worker():
??? for _ in range(100):
??????? print('{} is running\n'.format(threading.current_thread()),end='')
?
for _ in range(2):
??? threading.Thread(target=worker,name='worker-{}'.format('view')).start()
?
與linux的daemon守護(hù)進(jìn)程不一樣,linux是進(jìn)程級(jí)別,此處是線程;
進(jìn)程靠線程執(zhí)行代碼,至少有一個(gè)主線程,其它線程是工作線程;
主線程是第一個(gè)啟動(dòng)的線程;
父線程:如果線程A啟動(dòng)了線程B,A是B的父線程;
子線程:B是A的子線程;
?
查看Thread類源碼:
??? def __init__(self, group=None, target=None, name=None,
???????????????? args=(), kwargs=None, *, daemon=None):
注:
daemon,沒有主線程之說,都是當(dāng)前線程;
py構(gòu)造線程時(shí),可設(shè)置daemon屬性,這個(gè)屬性必須在t.start()方法前設(shè)置好;
?
線程有daemon屬性(僅py有),可顯式設(shè)置為True或False;也可不設(shè)置取默認(rèn)值None,如果不設(shè)置daemon就取當(dāng)前線程的daemon來設(shè)置它;
主線程是non-daemon線程(鐵律),即daemon=False,從主線程創(chuàng)建的所有線程不設(shè)置daemon屬性,則默認(rèn)都是daemon=False即non-daemon線程;
py程序在沒有活著的non-daemon線程運(yùn)行時(shí)退出(主線程kill掉所有活著的daemon線程,主線程退出),也就是剩下的只能是daemon線程,主線程才能退出,否則主線程只能等待(只有在有non-daemon線程,主線程一直等待);
?
daemon屬性,表示線程是否是daemon線程,這個(gè)值必須在t.start()前設(shè)置,否則引發(fā)RuntimeError異常;
isDaemon(),是否是daemon線程;
setDaemon(),設(shè)置為daemon線程,必須在t.start()之前設(shè)置;
?
簡(jiǎn)單來說,本來并沒有daemon thread,為簡(jiǎn)化程序員工作,讓他們不用去記錄和管理那些后臺(tái)進(jìn)程,創(chuàng)建了daemon thread概念,這個(gè)概念唯一的作用是,當(dāng)你把一個(gè)線程設(shè)為daemon,它會(huì)隨主線程的退出而退出;
daemon線程簡(jiǎn)化了程序員手動(dòng)關(guān)閉線程的工作;
?
daemon線程適用場(chǎng)景:
1、后臺(tái)任務(wù),如:發(fā)送心跳包、監(jiān)控等場(chǎng)景居多;
2、主線程工作才有用的線程,如主線程中維護(hù)著公共的資源,主線程已經(jīng)清理了,準(zhǔn)備退出,而工作線程使用這些資源工作也沒意義了,一起退出最合適;
3、隨時(shí)可被終止的線程;
如果主線程退出,想其它工作線程一起退出,就使用daemon=True來創(chuàng)建工作線程;
如,開啟一個(gè)線程,定時(shí)判斷web服務(wù)是否正常工作,主線程退出,工作線程也沒必要存在了,應(yīng)隨主線程一起退出,這種daemon線程一旦創(chuàng)建,就可忘記它了,只用關(guān)心主線程什么時(shí)候退出就行;
?
如果在non-daemon線程(A)中,對(duì)另一個(gè)daemon線程(B)使用了join方法,這個(gè)線程B設(shè)置daemon就沒意義了,因?yàn)?/span>A(non-daemon)總是要等待B的;
如果在一個(gè)daemon線程C中,對(duì)另一個(gè)daemon線程D使用了join方法,只能說明C要等待D,主線程退出,C和D不管是否結(jié)束,也不管它們誰(shuí)等誰(shuí),都要被殺掉;
?
例:
import threading
import logging
logging.basicConfig(level=logging.INFO)
?
def worker():
??? for _ in range(100):
??????? msg = '{} is running'.format(threading.current_thread())
??????? logging.info(msg)?? #logging.info(msg)和print()的內(nèi)容不規(guī)律的交替出現(xiàn),原因:多線程,誰(shuí)前誰(shuí)后不可預(yù)知,要看cpu運(yùn)行調(diào)度情況
??????? print('inner threading.enumerate(): {}'.format(threading.enumerate()))
?
threading.Thread(target=worker,name='worker-{}'.format('view')).start()?? #工作線程未設(shè)置daemon則用主線程的daemon=False(鐵律,主線程是non-daemon),主線程一直等待子線程執(zhí)行完才退出
print('end')
print('main threading.enumerate(): {}'.format(threading.enumerate()))?? #返回主線程+其它活著的工作線程
輸出:
……
INFO:root:<Thread(worker-view, started 16204)> is running
INFO:root:<Thread(worker-view, started 16204)> is running
end
inner threading.enumerate(): [<_MainThread(MainThread, started 17456)>, <Thread(worker-view, started 16204)>]
main threading.enumerate(): [<_MainThread(MainThread, started 17456)>, <Thread(worker-view, started 16204)>]
……
?
例:
# threading.Thread(target=worker,name='worker-{}'.format('view')).start()
threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True).start()?? #是daemon線程,主線程不會(huì)等待,直接殺掉daemon線程退出
print('end')
print('main threading.enumerate(): {}'.format(threading.enumerate()))
?
例:
logging.basicConfig(level=logging.INFO)
?
def worker():
??? threading.Thread(target=worker1,name='worker1-{}'.format('view')).start()?? #取當(dāng)前線程的daemon,即daemon=True
??? for _ in range(100):
??????? msg = '{} is running'.format(threading.current_thread())
??????? logging.info(msg)
?
def worker1():
??? for _ in range(100):
??????? msg = '$$$ {} is running'.format(threading.current_thread())
??????? logging.info(msg)
?
# threading.Thread(target=worker,name='worker-{}'.format('view')).start()
threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True).start()
# threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=False).start()?? #一旦該線程執(zhí)行到了,則主線程一直等待;如果沒有執(zhí)行到(當(dāng)前試驗(yàn)沒有執(zhí)行不到的情況)主線程直接退出了,如何改?加上time.sleep(0.0005)
# time.sleep(0.0005)
print('ending')
print(threading.enumerate())
?
t.join(timeout=None);
join()是線程的標(biāo)準(zhǔn)方法之一;
一個(gè)線程中調(diào)用另一個(gè)線程的join方法,調(diào)用者將被阻塞,直到被調(diào)用線程終止;
一個(gè)線程可被join多次;
調(diào)用誰(shuí)的join方法,就是join誰(shuí),就要等誰(shuí);
timeout參數(shù)很少用,指定調(diào)用者等待多久,沒有設(shè)置就一直等到被調(diào)用線程結(jié)束;
?
如果在non-daemon線程(A)中,對(duì)另一個(gè)daemon線程(B)使用了join方法,這個(gè)線程B設(shè)置daemon就沒意義了,因?yàn)?/span>A(non-daemon)總是要等待B的;
如果在一個(gè)daemon線程C中,對(duì)另一個(gè)daemon線程D使用了join方法,只能說明C要等待D,主線程退出,C和D不管是否結(jié)束,也不管它們誰(shuí)等誰(shuí),都要被殺掉;
?
例:
def worker():
??? for _ in range(50):
??????? msg = '{} is running'.format(threading.current_thread())
??????? logging.info(msg)
??????? t = threading.Thread(target=worker1,name='worker1-{}'.format('view'),daemon=True)
??????? t.start()
??????? # t.join()?? #如果有此句打印行數(shù)為5050
?
def worker1():
??? for _ in range(100):
??????? msg = '$$$ {} is running'.format(threading.current_thread())
??????? logging.info(msg)
?
t = threading.Thread(target=worker,name='worker-{}'.format('view'),daemon=True)
t.start()
t.join()?? #可理解為join(t),當(dāng)前線程是主線程,主線程和t聯(lián)合起來,主線程要等t執(zhí)行完才退出
?
例:
def bar():
??? while True:
??????? time.sleep(1)
??????? print('bar')
?
def foo():
??? print('t1 is daemon= {}'.format(threading.current_thread().isDaemon()))
??? t2 = threading.Thread(target=bar)
??? t2.start()
??? print('t2 is daemon= {}'.format(t2.isDaemon()))
??? t2.join()
?
t1 = threading.Thread(target=foo,daemon=True)
t1.start()
# t1.join()
time.sleep(2)
print('main thread exiting')
注:
等價(jià)于t1、t2都為daemon=False,non-daemon;
?
threading.local學(xué)習(xí)自java;
?
查看threading.local源碼:
class local:?? #注意local首字母為小寫
??? __slots__ = '_local__impl', '__dict__'
???????? ……
??? def __getattribute__(self, name):?? #對(duì)象所有屬性均攔截,重新構(gòu)建字典
?
class _localimpl:
??? def __init__(self):
??????? # The key used in the Thread objects' attribute dicts.
??????? # We keep it a string for speed but make it unlikely to clash with
??????? # a "real" attribute.
??????? self.key = '_threading_local._localimpl.' + str(id(self))
??????? # { id(Thread) -> (ref(Thread), thread-local dict) }
??????? self.dicts = {}
?
threading.local類構(gòu)建了一個(gè)大字典,即{ id(Thread) -> (ref(Thread), thread-local dict) },其元素的key是線程實(shí)例的內(nèi)存地址,其元素的value是二元組,二元組中的元素為(線程對(duì)象引用+每個(gè)線程的字典);
使用時(shí):
d[key][0].name
d[key][1][key]
?
通過threading.local()實(shí)例就可在不同線程中,安全的使用線程獨(dú)有的數(shù)據(jù),做到了線程間數(shù)據(jù)隔離,如同本地變量一樣安全;
?
多線程中,能不共享就不共享,用局部變量,局部變量與threading.local()比,局部變量用得多些;例如,外面?zhèn)鬟M(jìn)來的參數(shù),直接賦給本地變量,但對(duì)于引用類型來說不行(編程中經(jīng)常遇到外面?zhèn)鱽淼氖菍?duì)象,該對(duì)象通常是類型);
?
例:
def worker():
??? x = 0
??? for _ in range(100):
??????? time.sleep(0.0001)
??????? x += 1
??? print(threading.current_thread(),x)
?
for _ in range(5):
??? threading.Thread(target=worker).start()?? #線程有自己的棧,每個(gè)線程壓棧的x為函數(shù)內(nèi)局部作用域(局部變量),因此多線程里看到最終x值均為100
輸出:
<Thread(Thread-1, started 11216)> 100
<Thread(Thread-5, started 17792)> 100
<Thread(Thread-3, started 15924)> 100
<Thread(Thread-4, started 3248)> 100
<Thread(Thread-2, started 17804)> 100
?
例:
class A:
??? def __init__(self,x):
??????? self.x = x
?
a = A(0) ??#解決此問題(使用全局變量,還能保持每個(gè)線程使用不同的數(shù)據(jù))
def worker():
??? a.x = 0
??? for _ in range(100):
??????? time.sleep(0.0001)
??????? a.x += 1
??? print(threading.current_thread(),a.x)
??? print(a.__dict__)?? #每個(gè)線程有自己的字典,是隔離的
?
for _ in range(5):
??? threading.Thread(target=worker).start()?? #亂,不可預(yù)知,使用了全局對(duì)象,線程之間互相干擾,導(dǎo)致是錯(cuò)誤的結(jié)果
輸出:
<Thread(Thread-1, started 18216)> 483
<Thread(Thread-3, started 15560)> 493
<Thread(Thread-5, started 17464)> 496
<Thread(Thread-2, started 18268)> 497
<Thread(Thread-4, started 4676)> 498
?
例:
a = threading.local()
?
def worker():
?? ?a.x = 0
??? for _ in range(100):
??????? time.sleep(0.0001)
??????? a.x += 1
??? print(threading.current_thread(),a.x)
???????? print(a.__dict__)?? #每個(gè)線程有自己的字典,是隔離的
?
for _ in range(5):
??? threading.Thread(target=worker).start()?? #結(jié)果與使用局部變量效果一樣
輸出:
<Thread(Thread-4, started 11792)> 100
<Thread(Thread-2, started 12848)> 100
<Thread(Thread-1, started 17764)> 100
<Thread(Thread-3, started 16464)> 100
<Thread(Thread-5, started 16704)> 100
?
例:
X = 'abc'
ctx = threading.local()
ctx.x = 123
print(ctx,type(ctx),ctx.x)
?
def worker():
??? print(X)
??? print(ctx)
? ??print(ctx.x)
??? print('good job')
?
worker()
print('~'*30)
threading.Thread(target=worker).start()?? #ctx.x出錯(cuò),AttributeError,要在worker中重新定義ctx.x=567
輸出:
<_thread._local object at 0x000000000125DA40> <class '_thread._local'> 123
abc
<_thread._local object at 0x000000000125DA40>
123
good job
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
abc
<_thread._local object at 0x000000000125DA40>
Exception in thread Thread-1:
Traceback (most recent call last):
? File "D:\Python\Python35\lib\threading.py", line 914, in _bootstrap_inner
??? self.run()
? File "D:\Python\Python35\lib\threading.py", line 862, in run
??? self._target(*self._args, **self._kwargs)
? File "E:/git_practice/cmdb/example_threading.py", line 215, in worker
??? print(ctx.x)
AttributeError: '_thread._local' object has no attribute 'x'
?
定時(shí)器,延遲執(zhí)行,定義多久執(zhí)行一個(gè)函數(shù),繼承自threading.Thread;
?
查看源碼:
??? def __init__(self, interval, function, args=None, kwargs=None):
t.start()方法執(zhí)行后,Timer()對(duì)象處于等待狀態(tài),等待interval后,開始執(zhí)行function,實(shí)際是延遲執(zhí)行該線程而不是函數(shù);
non-daemon,繼承Thread;
Timer是Thread的子類,就是線程類,具有線程的能力和特征;它的實(shí)例是能夠延時(shí)執(zhí)行目標(biāo)函數(shù)的線程,在真正執(zhí)行目標(biāo)函數(shù)之前都可cancel它;
?
t.cancel()
如果在執(zhí)行函數(shù)之前的等待階段使用了t.cancel()方法,就會(huì)跳過,函數(shù)不會(huì)執(zhí)行;
并不是非要在t.start()之后,只要在延遲時(shí)間內(nèi)取消都可以;
查看源碼,通過event實(shí)現(xiàn)t.cancel();
?
例:
# def add():
#???? print(4 + 5)
#???? print(threading.enumerate())
# t = threading.Timer(3,add)
# t.setName('w1')
# t.start()
?
def add(x,y):
??? print(x + y)
??? print(threading.enumerate())
t1 = threading.Timer(2,add,args=(4,4))
t1.setName('w2')
t1.start()
# t1.cancel()?? #此句可不在t.start()之后,只要在延遲時(shí)間內(nèi)取消都可以;如果線程中add函數(shù)已經(jīng)開始執(zhí)行了,cancel就沒任何效果了
print(threading.enumerate())
time.sleep(3)
t1.cancel()?? #此時(shí)取消無效,3>2,add函數(shù)已經(jīng)執(zhí)行
輸出:
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
8
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
?
?
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。