溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

38線程1-Thread-local-Timer

發(fā)布時(shí)間:2020-06-19 22:36:52 來源:網(wǎng)絡(luò) 閱讀:368 作者:chaijowin 欄目:編程語(yǔ)言

?

目錄

并發(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

daemonnon-daemon... 11

t.join() 14

threading.local類:... 15

threading.Timer類:... 18

?

?

?

并發(fā)和并行:

parallel,并行:

同時(shí)做某些事,可互不干擾的同一時(shí)刻做幾件事;

?

例:

高速公路的車道,所有車輛(數(shù)據(jù))可互不干擾的在自己的車道上奔跑(傳輸);在同一時(shí)刻,每條車道上可能同時(shí)有車輛在跑,是同時(shí)發(fā)生的概念;

?

在一定程度上,并行可解決并發(fā)問題;并行并不是解決并發(fā)的最佳方案;

?

?

concurrency,并發(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í)雙向車道通行,通行車輛增多,能滿足一定的通行需求;

?

?

并發(fā)的解決:

食堂打飯模型:

中午12點(diǎn),開飯,都涌向食堂,這就是并發(fā),如果人很多,就是高并發(fā);

?

1、隊(duì)列、緩沖區(qū):

假設(shè)只有一個(gè)窗口,陸續(xù)涌入食堂的,排隊(duì)打菜是比較好的方式;

排隊(duì)就是把人排成隊(duì)列,先進(jìn)先出,解決了資源使用的問題;

排成的隊(duì)列,其實(shí)就是一個(gè)緩沖地帶,就是緩沖區(qū);

假設(shè)女生優(yōu)先,那么這個(gè)窗口就得是兩隊(duì),只要有女生就先打飯,男生排隊(duì)等著,女生隊(duì)伍就是一個(gè)優(yōu)先隊(duì)列;

?

2、爭(zhēng)搶:

只開一個(gè)窗口,有可能沒有秩序,也就是誰(shuí)擠進(jìn)去就給誰(shuí)打飯;

擠到窗口的人占據(jù)窗口,直到打到飯菜離開;

其他人繼續(xù)爭(zhēng)搶,會(huì)有一個(gè)人正占據(jù)窗口,可以視為鎖定窗口,窗口就不能為其他人提供了服務(wù)了,這是一種鎖機(jī)制;

搶到資源就上鎖,排他性鎖,其他人只能等候;

爭(zhēng)搶也是一種高并發(fā)解決方案,但不好,因?yàn)橛锌赡苡腥撕荛L(zhǎng)時(shí)間搶不到;

?

3、預(yù)處理:

如果排長(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ù)處理思想,緩存常用;

?

4、并行:

成百上千人同時(shí)來吃飯,一個(gè)隊(duì)伍搞不定的,多開打飯窗口形成多個(gè)隊(duì)列,如同開多個(gè)車道一樣,并行打菜;

開窗口就得擴(kuò)大食堂,得雇人在每一個(gè)窗口提供服務(wù),造成成本上升;

日??赏ㄟ^購(gòu)買更多服務(wù)器,或多開進(jìn)程實(shí)現(xiàn)并行處理,來解決并發(fā)問題;

這些都是水平擴(kuò)展思想;

?

5、提速:

提高單個(gè)窗口的打飯速度;

打飯人員提高工作技能,或?yàn)閱蝹€(gè)窗口配備更多的服務(wù)人員,都是提速的辦法;

這是一種垂直擴(kuò)展思想;

?

6、消息中間件:

北京地鐵站外九曲回腸走廊,緩沖人流,進(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車道快;

?

?

?

進(jìn)程和線程:

在實(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、寄存器集合、堆棧組成;

?

processthread理解:

現(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)程;

?

?

?

線程的狀態(tài):

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,終止,線程完成、或退出、或被取消;


?

?

?

py的線程開發(fā):

使用標(biāo)準(zhǔn)庫(kù)threading;

?

threading.Thread類:

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ù)、中斷了;

?

線程啟動(dòng):

例:

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 oncet.start()只能一次,如果非要再寫一次必須先實(shí)例化t=threading.Thread(...)再啟動(dòng)t.start()

t1 = threading.Thread(target=worker1, name='w2')?? #對(duì)于1cpuserver,多線程是假并行

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

?

threading的屬性和方法:

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

?

Thread實(shí)例的屬性和方法:

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()t.run()

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

?

daemonnon-daemon

linuxdaemon守護(hù)進(jìn)程不一樣,linux是進(jìn)程級(jí)別,此處是線程;

進(jìn)程靠線程執(zhí)行代碼,至少有一個(gè)主線程,其它線程是工作線程;

主線程是第一個(gè)啟動(dòng)的線程;

父線程:如果線程A啟動(dòng)了線程BAB的父線程;

子線程:BA的子線程;

?

查看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è)置為TrueFalse;也可不設(shè)置取默認(rèn)值None,如果不設(shè)置daemon就取當(dāng)前線程的daemon來設(shè)置它;

主線程是non-daemon線程(鐵律),即daemon=False,從主線程創(chuàng)建的所有線程不設(shè)置daemon屬性,則默認(rèn)都是daemon=Falsenon-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>Anon-daemon)總是要等待B的;

如果在一個(gè)daemon線程C中,對(duì)另一個(gè)daemon線程D使用了join方法,只能說明C要等待D,主線程退出,CD不管是否結(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()

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>Anon-daemon)總是要等待B的;

如果在一個(gè)daemon線程C中,對(duì)另一個(gè)daemon線程D使用了join方法,只能說明C要等待D,主線程退出,CD不管是否結(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=Falsenon-daemon;

?

threading.local類:

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'

?

threading.Timer類:

定時(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;

TimerThread的子類,就是線程類,具有線程的能力和特征;它的實(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)>]

?

?


向AI問一下細(xì)節(jié)

免責(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)容。

AI