您好,登錄后才能下訂單哦!
?
目錄
并發和并行:... 1
parallel,并行:... 1
concurrency,并發:... 2
并發的解決:... 2
1、隊列、緩沖區:... 2
2、爭搶:... 3
3、預處理:... 3
4、并行:... 3
5、提速:... 3
6、消息中間件:... 3
進程和線程:... 4
線程的狀態:... 5
py的線程開發:... 5
threading.Thread類:... 6
線程啟動:... 6
線程退出:... 7
線程的傳參:... 8
threading的屬性和方法:... 8
Thread實例的屬性和方法:... 9
t.start()和t.run():... 9
線程安全:... 11
daemon和non-daemon:... 11
t.join() 14
threading.local類:... 15
threading.Timer類:... 18
?
?
?
同時做某些事,可互不干擾的同一時刻做幾件事;
?
例:
高速公路的車道,所有車輛(數據)可互不干擾的在自己的車道上奔跑(傳輸);在同一時刻,每條車道上可能同時有車輛在跑,是同時發生的概念;
?
在一定程度上,并行可解決并發問題;并行并不是解決并發的最佳方案;
?
?
也是同時做某些事,但是強調,同一時段做了幾件事;
?
例:
鄉村公路一條車道,半幅路面出現了坑,交警指揮車輛走另外半幅路面,一個方向放行3min,換另一個方向,發生了車輛同時要通過路面的事件,這就是并發;
?
并發是在一時間點有很多事要做,并發太大,如何做?怎么做并不知道;
?
隊列、緩沖區:
鄉村公路窄,車輛就會排隊,所以排隊是一種天然解決并發的辦法,車輛排隊的緩沖地帶就是緩沖區;
?
并行:
鄉村公路窄,拓寬成雙向四車道,可同時雙向車道通行,通行車輛增多,能滿足一定的通行需求;
?
?
食堂打飯模型:
中午12點,開飯,都涌向食堂,這就是并發,如果人很多,就是高并發;
?
假設只有一個窗口,陸續涌入食堂的,排隊打菜是比較好的方式;
排隊就是把人排成隊列,先進先出,解決了資源使用的問題;
排成的隊列,其實就是一個緩沖地帶,就是緩沖區;
假設女生優先,那么這個窗口就得是兩隊,只要有女生就先打飯,男生排隊等著,女生隊伍就是一個優先隊列;
?
只開一個窗口,有可能沒有秩序,也就是誰擠進去就給誰打飯;
擠到窗口的人占據窗口,直到打到飯菜離開;
其他人繼續爭搶,會有一個人正占據窗口,可以視為鎖定窗口,窗口就不能為其他人提供了服務了,這是一種鎖機制;
搶到資源就上鎖,排他性鎖,其他人只能等候;
爭搶也是一種高并發解決方案,但不好,因為有可能有人很長時間搶不到;
?
如果排長隊的原因,是由于每個人打菜等候時間長,因為要吃的菜沒有,需要現做,沒打著飯不走開,鎖定著窗口;
可以提前統計大多數最愛吃的菜品,最愛吃的80%的熱門菜提前做好,保證供應,20%的冷門菜現做;
這樣大多數人,就算鎖定窗口,也很快就釋放窗口了;
一種提前加載用戶需要的數據的思路,預處理思想,緩存常用;
?
成百上千人同時來吃飯,一個隊伍搞不定的,多開打飯窗口形成多個隊列,如同開多個車道一樣,并行打菜;
開窗口就得擴大食堂,得雇人在每一個窗口提供服務,造成成本上升;
日常可通過購買更多服務器,或多開進程實現并行處理,來解決并發問題;
這些都是水平擴展思想;
?
提高單個窗口的打飯速度;
打飯人員提高工作技能,或為單個窗口配備更多的服務人員,都是提速的辦法;
這是一種垂直擴展思想;
?
北京地鐵站外九曲回腸走廊,緩沖人流,進去之后再多口安檢進站;
rabbitmq、activemq(apache)、rocketmq(ali apache)、kafka(apache)等;
?
以上是最常用的解決方案,一般來說不同的并發場景用不同的策略,而策略可能是多種方式的優化組合;
如,多開食堂(多地),可把食堂建設到宿舍生活區(就近),技術來源于生活;
?
注:
如果線程在單cpu上處理,就不是并行了;
一般server都是多cpu的,服務的部署往往是多機的、分布式的,這都是并行處理;
真正的好系統,是串行、并行共存的;
串行、并行是解決并發的手段之一,不可厚此薄彼;
串行、并行哪個快?問題本身有問題,類似1車道快還是4車道快;
?
?
?
在實現了線程的OS中,線程是OS能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位,一個程序的執行實例就是一個進程;
?
process,進程:
是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是OS結構的基礎;
?
進程和程序的關系:
程序是源代碼編譯后的文件,而這些文件存放在disk上,當程序被OS加載到內存中,就是進程,進程中存放著指令和數據(資源),它也是線程的容器;
?
linux進程有父進程、子進程;
win的進程是平等關系;
?
thread,線程:
有時被稱為light weight process,LWP,輕量級進程,線程是程序執行流的最小單元;
一個標準的線程由線程ID、當前指令指針PC、寄存器集合、堆棧組成;
?
process、thread理解:
現代OS提出進程的概念,每一個進程都認為自己獨占所有的計算機硬件資源;
進程就是獨立的王國,進程間不可以隨便的共享數據;
進程是國,線程是省;
線程就是省份,同一個進程內的線程可共享進程的資源,每一個線程擁有自己獨立的堆棧;
進程是容器,進程內的線程是干活的;
線程是程序執行流的最小單元,基本單元是進程;
?
py中的進程和線程:
進程會啟動一個解釋器進程,線程共享一個解釋器進程;
?
?
?
ready,就緒態,線程能夠運行,但在等待被調度,可能線程剛剛創建啟動,或剛剛從阻塞中恢復,或被其它線程搶占;
running,運行態,線程正在運行;
blocked,阻塞態,線程等待外部事件發生而無法運行,如IO操作;
terminated,終止,線程完成、或退出、或被取消;
?
?
?
使用標準庫threading;
?
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None)
target,線程調用的對象,就是目標函數;
name,為線程起個名,可重名,區分是靠線程ID;
args,為目標函數傳遞實參,元組;
kwargs,為目標函數傳遞關鍵字參數,字典;
?
py的線程沒有優先級,沒有線程組的概念,也不能被銷毀、停止、掛起,也就沒有恢復、中斷了;
?
例:
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')?? #實例化一個線程對象
t.start()?? #線程啟動,每個線程必須且只能執行該方法1次
# print('~'*30)
# t.start()?? #RuntimeError: threads can only be started once,t.start()只能一次,如果非要再寫一次必須先實例化t=threading.Thread(...)再啟動t.start()
t1 = threading.Thread(target=worker1, name='w2')?? #對于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沒有提供線程退出的方法,線程在如下情況時退出:
1、線程函數內語句執行完畢;
2、線程函數中拋出未處理的異常;
例:
import threading
import time
?
def worker2():
??? count = 0
??? while True:?? #通過在循環中加標記,當某一變量為指定值時退出循環
??????? 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
?
本質上就是函數傳參,線程傳參和函數傳參沒什么區別;
例:
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(),返回當前線程對象;
main_thread(),返回主線程對象;
active_count(),當前處于alive狀態(一旦被cpu調度才是活動狀態ready<->running)的線程個數;
enumerate(),返回所有活著的線程的列表,不包括已終止的線程和未開始的線程;
get_ident(),返回當前線程的ID,非0整數;
?
例:
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')?? #多線程無法確定誰前誰后,每個都有機會被執行到
# 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,只是個名字,是個標識,可重名,getName()獲取,setName()設置;
t.ident,線程ID,是非0整數,線程啟動后才會有ID,否則為None;線程退出,此ID依舊可訪問,此ID可重復使用;t.name可重復,t.ident必須唯一但可在線程退出后再利用;
t.is_alive(),返回線程是否活著,True|False;
?
t.start(),用于多線程場景,一執行到t1.start()則開啟一個新的工作線程,threading.current_thread()為<MyThread(w1, started 17760)>,主線程1個工作線程多個;
t.run(),運行線程函數,僅是普通函數調用,不會開啟新的線程,在當前線程中順序執行,threading.current_thrad()為<_MainThread(MainThread, started 15384)>,先執行t1.run()再執行t2.run();
?
注:多線程:
一個進程中有多個線程,實現并發;
一個進程至少有一個主線程,其它線程為工作線程;
一個進程至少有一個線程,作為程序入口,這個線程就是主線程;
多線程爭用同一個資源(打飯時很多人爭一個窗口),解決:加鎖;
父線程不管理子線程,父、子只是為了分清誰啟動的誰;
父線程消亡,子線程仍可繼續運行;
主線程結束時,可殺掉工作線程(子線程);
?
例:
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
?
多線程的執行結果,有時是意想不到的,即線程不安全,在各種高級語言中都必須面對;
如下例在pycharm中和ipython中分別執行,pycharm中整整齊齊,而ipython中不規整,ipython中運行的效果就是線程不安全;
?
其它語言中有:
線程安全函數;
線程不安全函數(要加鎖);
線程安全類;
線程不安全類(加各種鎖);
?
在多線程或特殊場景下應用,能不加鎖就不加,否則效率極差;
?
例:
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守護進程不一樣,linux是進程級別,此處是線程;
進程靠線程執行代碼,至少有一個主線程,其它線程是工作線程;
主線程是第一個啟動的線程;
父線程:如果線程A啟動了線程B,A是B的父線程;
子線程:B是A的子線程;
?
查看Thread類源碼:
??? def __init__(self, group=None, target=None, name=None,
???????????????? args=(), kwargs=None, *, daemon=None):
注:
daemon,沒有主線程之說,都是當前線程;
py構造線程時,可設置daemon屬性,這個屬性必須在t.start()方法前設置好;
?
線程有daemon屬性(僅py有),可顯式設置為True或False;也可不設置取默認值None,如果不設置daemon就取當前線程的daemon來設置它;
主線程是non-daemon線程(鐵律),即daemon=False,從主線程創建的所有線程不設置daemon屬性,則默認都是daemon=False即non-daemon線程;
py程序在沒有活著的non-daemon線程運行時退出(主線程kill掉所有活著的daemon線程,主線程退出),也就是剩下的只能是daemon線程,主線程才能退出,否則主線程只能等待(只有在有non-daemon線程,主線程一直等待);
?
daemon屬性,表示線程是否是daemon線程,這個值必須在t.start()前設置,否則引發RuntimeError異常;
isDaemon(),是否是daemon線程;
setDaemon(),設置為daemon線程,必須在t.start()之前設置;
?
簡單來說,本來并沒有daemon thread,為簡化程序員工作,讓他們不用去記錄和管理那些后臺進程,創建了daemon thread概念,這個概念唯一的作用是,當你把一個線程設為daemon,它會隨主線程的退出而退出;
daemon線程簡化了程序員手動關閉線程的工作;
?
daemon線程適用場景:
1、后臺任務,如:發送心跳包、監控等場景居多;
2、主線程工作才有用的線程,如主線程中維護著公共的資源,主線程已經清理了,準備退出,而工作線程使用這些資源工作也沒意義了,一起退出最合適;
3、隨時可被終止的線程;
如果主線程退出,想其它工作線程一起退出,就使用daemon=True來創建工作線程;
如,開啟一個線程,定時判斷web服務是否正常工作,主線程退出,工作線程也沒必要存在了,應隨主線程一起退出,這種daemon線程一旦創建,就可忘記它了,只用關心主線程什么時候退出就行;
?
如果在non-daemon線程(A)中,對另一個daemon線程(B)使用了join方法,這個線程B設置daemon就沒意義了,因為A(non-daemon)總是要等待B的;
如果在一個daemon線程C中,對另一個daemon線程D使用了join方法,只能說明C要等待D,主線程退出,C和D不管是否結束,也不管它們誰等誰,都要被殺掉;
?
例:
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()的內容不規律的交替出現,原因:多線程,誰前誰后不可預知,要看cpu運行調度情況
??????? print('inner threading.enumerate(): {}'.format(threading.enumerate()))
?
threading.Thread(target=worker,name='worker-{}'.format('view')).start()?? #工作線程未設置daemon則用主線程的daemon=False(鐵律,主線程是non-daemon),主線程一直等待子線程執行完才退出
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線程,主線程不會等待,直接殺掉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()?? #取當前線程的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()?? #一旦該線程執行到了,則主線程一直等待;如果沒有執行到(當前試驗沒有執行不到的情況)主線程直接退出了,如何改?加上time.sleep(0.0005)
# time.sleep(0.0005)
print('ending')
print(threading.enumerate())
?
t.join(timeout=None);
join()是線程的標準方法之一;
一個線程中調用另一個線程的join方法,調用者將被阻塞,直到被調用線程終止;
一個線程可被join多次;
調用誰的join方法,就是join誰,就要等誰;
timeout參數很少用,指定調用者等待多久,沒有設置就一直等到被調用線程結束;
?
如果在non-daemon線程(A)中,對另一個daemon線程(B)使用了join方法,這個線程B設置daemon就沒意義了,因為A(non-daemon)總是要等待B的;
如果在一個daemon線程C中,對另一個daemon線程D使用了join方法,只能說明C要等待D,主線程退出,C和D不管是否結束,也不管它們誰等誰,都要被殺掉;
?
例:
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()?? #如果有此句打印行數為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),當前線程是主線程,主線程和t聯合起來,主線程要等t執行完才退出
?
例:
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')
注:
等價于t1、t2都為daemon=False,non-daemon;
?
threading.local學習自java;
?
查看threading.local源碼:
class local:?? #注意local首字母為小寫
??? __slots__ = '_local__impl', '__dict__'
???????? ……
??? def __getattribute__(self, name):?? #對象所有屬性均攔截,重新構建字典
?
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類構建了一個大字典,即{ id(Thread) -> (ref(Thread), thread-local dict) },其元素的key是線程實例的內存地址,其元素的value是二元組,二元組中的元素為(線程對象引用+每個線程的字典);
使用時:
d[key][0].name
d[key][1][key]
?
通過threading.local()實例就可在不同線程中,安全的使用線程獨有的數據,做到了線程間數據隔離,如同本地變量一樣安全;
?
多線程中,能不共享就不共享,用局部變量,局部變量與threading.local()比,局部變量用得多些;例如,外面傳進來的參數,直接賦給本地變量,但對于引用類型來說不行(編程中經常遇到外面傳來的是對象,該對象通常是類型);
?
例:
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()?? #線程有自己的棧,每個線程壓棧的x為函數內局部作用域(局部變量),因此多線程里看到最終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) ??#解決此問題(使用全局變量,還能保持每個線程使用不同的數據)
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__)?? #每個線程有自己的字典,是隔離的
?
for _ in range(5):
??? threading.Thread(target=worker).start()?? #亂,不可預知,使用了全局對象,線程之間互相干擾,導致是錯誤的結果
輸出:
<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__)?? #每個線程有自己的字典,是隔離的
?
for _ in range(5):
??? threading.Thread(target=worker).start()?? #結果與使用局部變量效果一樣
輸出:
<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出錯,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.Thread;
?
查看源碼:
??? def __init__(self, interval, function, args=None, kwargs=None):
t.start()方法執行后,Timer()對象處于等待狀態,等待interval后,開始執行function,實際是延遲執行該線程而不是函數;
non-daemon,繼承Thread;
Timer是Thread的子類,就是線程類,具有線程的能力和特征;它的實例是能夠延時執行目標函數的線程,在真正執行目標函數之前都可cancel它;
?
t.cancel()
如果在執行函數之前的等待階段使用了t.cancel()方法,就會跳過,函數不會執行;
并不是非要在t.start()之后,只要在延遲時間內取消都可以;
查看源碼,通過event實現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()之后,只要在延遲時間內取消都可以;如果線程中add函數已經開始執行了,cancel就沒任何效果了
print(threading.enumerate())
time.sleep(3)
t1.cancel()?? #此時取消無效,3>2,add函數已經執行
輸出:
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
8
[<_MainThread(MainThread, started 14560)>, <Timer(w2, started 17696)>]
?
?
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。