孤荷凌寒自学python第三十八天初识python的线程控制
?
(完整學習過程屏幕記錄視頻地址在文末,手寫筆記在文末)
一、線程
在操作系統(tǒng)中存在著很多的可執(zhí)行的應(yīng)用程序,每個應(yīng)用程序啟動后,就可以看著是一個進程,當打開WINDOWS任務(wù)管理器時,在任務(wù)管理器的進程選項卡中列出的就是一個一個的進程,基本上每個應(yīng)用程序都對應(yīng)著至少一個進程。
在同一進程中,也許同時在做著不止一件事情,比如在向程序界面上顯示信息和接受信息的同時,程序也在和遠端服務(wù)器通信讀取數(shù)據(jù),則這兒至少有兩個線程運行在同一個進程中。
我的簡單理解是,在同一個操作系統(tǒng)的進程中,可以同時執(zhí)行多個線程的任務(wù)。
?
二、threading模塊
threading模塊是Python?中主要的線程控制模塊。
使用threading模塊前必須先聲明引用:
import threading
?
三、threading模塊的常見子類與屬性和方法
1
threading.activeCount()
執(zhí)行此方法返回當前進程下所有活動的線程總數(shù)。
?
2
threading.enumerate()
此方法直接返回一個 迭代器,迭代器中包含了,所有當前進程下的所有線程的信息。
在實際測試中,發(fā)現(xiàn)在循環(huán)打印時,總是不打印出第0個線程的信息,沒有核準原因。
見后面的測試代碼與結(jié)果。
?
3
threading.Thread
這是threading模塊下的子模塊(類),特別注意Thread模塊的首字母是大寫的。這是最容易疏忽之處。
?
四、獲取或定義得到一個threading.Thread線程對象
可以通過以下方式得到threading.Thread線程對象
1.第一種:
thread對象=threading.Thread(target=要讓新線程執(zhí)行的函數(shù)名?, args=這個函數(shù)需要的參數(shù)組成的元組)
測試代碼:
def?f1(n):
??? strtime=str(datetime.now())
????print('線程'?+ n +?'正在運行中....線程啟動于:'?+strtime)
?
t=threading.Thread(target=f1,args=('t1',))
上面代碼中,我創(chuàng)建了一個線程對象?t ,這個線程如果啟動,將執(zhí)行函數(shù)f1定義好的內(nèi)部代碼塊。
Thread類的建構(gòu)代碼(初始化函數(shù))中,要求傳遞的兩個實參,只能按【關(guān)鍵字參數(shù)】形式傳遞進去。
?
2.第二種
thread對象=threading.Thread(target=要調(diào)用的作為函數(shù)用的類名(此類的初始化方法需要的參數(shù)列表))
測試代碼:
class?myt(object):
?
????def?__init__(self,n):
????????self.n=n
?
????def?__call__(self):
??????? strtime=str(datetime.now())
????????print('線程'?+?self.n +?'正在運行中....線程啟動于:'?+strtime)
?
t=threading.Thread(target=myt('t1'))
這種方式中,線程t啟動后,將執(zhí)行的是一個可以當著函數(shù)來調(diào)用的類myt.
myt是一個可以被 當著函數(shù)來使用的類,因為在類的內(nèi)部代碼塊中定義有:__call__()私有方法。
與第一種方式不同的地方在于,這次只向Thread初始化方法傳遞了一個關(guān)鍵字實參target。
?
3.第三種方式
直接寫一個新的繼承自threading.Thread類的子類,然后,用新的類來初始化得到一個thread對象。
測試代碼:
class?myt(threading.Thread):
?
????def?__init__(self,n):
??????? threading.Thread.__init__(self)
????????self.n=n
?
????def?run(self):
??????? strtime=str(datetime.now())
????????print('線程'?+?self.n +?'正在運行中....線程啟動于:'?+strtime)
?
t=myt('t1')
首先建構(gòu)了一個繼自threading.Thread的類?myt,這個類的特點有:
(1)在子類的__init__()方法中,必須 先調(diào)用父類的__init__()方法,在我的上機測試過程中(見過程屏幕錄像,這兒反復出錯,結(jié)果發(fā)現(xiàn)就是沒有先調(diào)用父類的__init()__方法,引發(fā)的錯誤。
而原因在測試當時沒有思考出來,所以大家在我的測試屏幕錄像中可以看到?jīng)]有想明白,現(xiàn)在思考下,發(fā)現(xiàn)可能是因為在threading.Thread類本身的__init__()中要執(zhí)行非常重要的初始化操作,才能保證thread本類實例化后的對象功能可用,我找到了threading.Thread類本身的__init__()方法的代碼如下:
?
????def?__init__(self,?group=None,?target=None,?name=None,
?????????????????args=(),?kwargs=None, *,?daemon=None):
????????"""Thisconstructor should always be called with keyword arguments. Arguments are:
?
??????? *group* should be None; reserved forfuture extension when a ThreadGroup
??????? class is implemented.
?
??????? *target* is the callable object to beinvoked by the run()
??????? method. Defaults to None, meaningnothing is called.
?
??????? *name* is the thread name. By default,a unique name is constructed of
??????? the form "Thread-N" where Nis a small decimal number.
?
??????? *args* is the argument tuple for thetarget invocation. Defaults to ().
?
??????? *kwargs* is a dictionary of keywordarguments for the target
??????? invocation. Defaults to {}.
?
??????? If a subclass overrides theconstructor, it must make sure to invoke
??????? the base class constructor (Thread.__init__())before doing anything
??????? else to the thread.
?
??????? """
????????assert?group?is?None,?"groupargument must be None for now"
????????if?kwargs?is?None:
??????????? kwargs = {}
????????self._target= target
????????self._name =str(name?or?_newname())
????????self._args =args
????????self._kwargs= kwargs
????????if?daemon?is?not?None:
????????????self._daemonic= daemon
????????else:
????????????self._daemonic= current_thread().daemon
????????self._ident=?None
????????self._tstate_lock=?None
????????self._started= Event()
????????self._is_stopped=?False
????????self._initialized=?True
????????# sys.stderr is notstored in the class like
????????# sys.exc_infosince it can be changed between instances
????????self._stderr= _sys.stderr
????????# For debugging and_after_fork()
??????? _dangling.add(self)
?
以上代碼可以證明我的猜測是正確的,因此在測試屏幕錄像中我沒有想通的問題現(xiàn)在基本上有答案了,當然正確與否希望大家批評指正。
(2)在定義繼承自threading.Thread類的子類中,__call__()方法 需要被 改名為?run()方法。
?
五、threading.Thread對象的主要屬性與方法有:
1 name屬性
此屬性可讀寫,用以設(shè)置和獲取線程對象的名稱。
?
2 getName()方法
用于獲取線程對象的名稱。
?
3 setName()方法
用于設(shè)置線程對象的名稱。
?
4 ident
此屬性是只讀的,獲取線程對象的標識碼,標識碼是數(shù)字。
?
5 is_alive()??isAlive()
這兩個方法用于判斷線程對象是否正在運行(存活),返回布爾對象。
?
6 join()
這是線程對象非常 重要 的方法,此方法有一個可選 形參?timeout
執(zhí)行線程的此方法后,會將調(diào)用 此線程的上級線程 阻塞,然后等待此線程執(zhí)行完成,如果為join()方法設(shè)置了timeout時間,那么從此線程開始執(zhí)行起計時,達到timeout指定的時間限額時,將自動解除對上級線程的阻塞。
在今天 的測試過程中,沒有著重測試此方法的具體功用,理解可能不完全準確。
?
7 start()
線程執(zhí)行此方法后,線程就開始執(zhí)行指定任務(wù),同時線程的isAlive標識將顯示為True
此外,我沒有發(fā)現(xiàn)線程有stop()方法,目前也沒有研究到中止指定線程執(zhí)行的其它方法。
?
測試代碼一 :
importthreading
from?datetimeimport?datetime
?
intcount=3
?
class?myt(object):
?
????def?__init__(self,n):
????????self.n=n
?
????def?__call__(self):
??????? strtime=str(datetime.now())
????????print('線程'?+?self.n +?'正在運行中....線程啟動于:'?+strtime)
?
def?main():
??? threads=[]
??? x=range(intcount)
????for?n?in?x:
??????? t=threading.Thread(target=myt('t'?+?str(n)))
????????#if n==1:
????????#??? t.setDaemon(True)
??????? t.name='t'?+?str(n)
??????? threads.append(t)
?
????for?n?in?x:
??????? threads[n].start()
?
????#print(threading.activeCount())
????for?item?inthreading.enumerate():
????????print(item)
?
????print('--------')
?
????for?item?inthreads:
????????print(item)
?
????print(threads[0].isAlive())
?
????for?n?in?x:
??????? threads[n].join()
?
if?__name__=='__main__':
??? main()
執(zhí)行結(jié)果:
線程t0正在運行中....線程啟動于:2018-08-1617:31:08.939618
線程t1正在運行中....線程啟動于:2018-08-1617:31:08.939618
線程t2正在運行中....線程啟動于:2018-08-1617:31:08.940647
<_MainThread(MainThread, started 4436)>
?
<Thread(t1, stopped 2444)>
<Thread(t2, stopped 8676)>
--------
<Thread(t0, stopped 3188)>
<Thread(t1, stopped 2444)>
<Thread(t2, stopped 8676)>
False
測試代碼二:
importthreading
fromdatetime?import?datetime
?
intcount=3
?
class?myt(threading.Thread):
?
????def?__init__(self,n):
??????? threading.Thread.__init__(self)
????????self.n=n
?
????def?run(self):
??????? strtime=str(datetime.now())
????????print('線程'?+?self.n +?'正在運行中....線程啟動于:'?+strtime)
?
def?main():
??? threads=[]
??? x=range(intcount)
????for?n?in?x:
??????? t=myt('t'?+?str(n))
????????#if n==1:
????????#??? t.setDaemon(True)
??????? t.name='t'?+?str(n)
??????? threads.append(t)
?
????for?n?in?x:
??????? threads[n].start()
?
????#print(threading.activeCount())
????for?item?inthreading.enumerate():
????????print(item)
?
????print('--------')
?
????for?item?inthreads:
????????print(item)
?
????print(threads[0].isAlive())
?
????for?n?in?x:
??????? threads[n].join()
?
if?__name__=='__main__':
??? main()
執(zhí)行結(jié)果:
線程t0正在運行中....線程啟動于:2018-08-1617:32:36.893226
線程t1正在運行中....線程啟動于:2018-08-1617:32:36.894223
線程t2正在運行中....線程啟動于:2018-08-1617:32:36.895221
<_MainThread(MainThread, started 8884)>
?
<myt(t2, stopped 8940)>
--------
<myt(t0, stopped 7152)>
<myt(t1, stopped 8744)>
<myt(t2, stopped 8940)>
False
注意執(zhí)行結(jié)果中,threading.enumerate()方法返回的迭代器在循環(huán)取出對象時,總沒有完整打印,且有空行。這點沒有思考出答案。搜索網(wǎng)絡(luò)沒有找到答案,還有待繼續(xù)思考,并懇請高手予以指點。
?
——————————
今天整理的學習筆記完成,最后例行說明下我的自學思路:
根據(jù)過去多年我自學各種編程語言的經(jīng)歷,認為只有真正體驗式,解決實際問題式的學習才會有真正的效果,即讓學習實際發(fā)生。在2004年的時候我開始在一個鄉(xiāng)村小學自學電腦 并學習vb6編程語言,沒有學習同伴,也沒有高師在上,甚至電腦都是孤島(鄉(xiāng)村那時還沒有網(wǎng)絡(luò)),有的只是一本舊書,在痛苦的自學摸索中,我找到適應(yīng)自己零基礎(chǔ)的學習方法:首先是每讀書的一小節(jié)就作相應(yīng)的手寫筆記,第二步就是上機測試每一個筆記內(nèi)容是否實現(xiàn),其中會發(fā)現(xiàn)書中講的其實有出入或錯誤,第三步就是在上機測試之后,將筆記改為電子版,形成最終的修訂好的正確無誤的學習筆記。
通過反復嘗試錯誤,在那個沒有分享與交流的黑暗時期我摸黑學會了VB6,爾后接觸了其它語言,也曾聽過付費視頻課程,結(jié)果發(fā)現(xiàn)也許自己學歷果然太低,就算是零基礎(chǔ)的入門課程,其實也難以跟上進度,講師的教學多數(shù)出現(xiàn)對初學者的實際情況并不了解的情況,況且學習者的個體也存在差異呢?當然更可怕的是收費課程的價格往往是自己難以承受的。
于是我的所有編程學習都改為了自學,繼續(xù)自己的三步學習筆記法的學習之路。
當然自學的最大問題是會走那么多的彎路,沒有導師直接輸入式的教學來得直接,好在網(wǎng)絡(luò)給我們帶來無限搜索的機會,大家在網(wǎng)絡(luò)上的學習日志帶給我們共享交流的機會,而QQ群等交流平臺、網(wǎng)絡(luò)社區(qū)的成立,我們可以一起自學,互相批評交流,也可以獲得更有效,更自主的自學成果。
于是我以人生已過半的年齡,決定繼續(xù)我的編程自學之路,開始學習python,只希望與大家共同交流,一個人的獨行是可怕的,只有一群人的共同前進才是有希望的。
誠摯期待您的交流分享批評指點!歡迎聯(lián)系我加入從零開始的自學聯(lián)盟。
這個時代互聯(lián)網(wǎng)成為了一種基礎(chǔ)設(shè)施的存在,于是本來在孤獨學習之路上的我們變得不再孤獨,因為網(wǎng)絡(luò)就是一個新的客廳,我們時刻都可以進行沙龍活動。
非常樂意能與大家一起交流自己自學心得和發(fā)現(xiàn),更希望大家能夠?qū)ξ覍W習過程中的錯誤給予指點——是的,這樣我就能有許多免費的高師了——這也是分享時代,社區(qū)時代帶來的好福利,我相信大家會的,是吧!
?
根據(jù)完全共享的精神,開源互助的理念,我的個人自學錄制過程是全部按4K高清視頻錄制的,從手寫筆記到驗證手寫筆記的上機操作過程全程錄制,但因為4K高清文件太大均超過5G以上,所以無法上傳至網(wǎng)絡(luò),如有需要可聯(lián)系我QQ578652607對傳,樂意分享。上傳分享到百度網(wǎng)盤的只是壓縮后的720P的視頻。
?
我的學習過程錄像百度盤地址分享如下:(清晰度:1280x720)
鏈接:https://pan.baidu.com/s/1n00vs2zpHiMCCf5eqykGrg?
提取碼:bgu2
?
Bilibili:
https://www.bilibili.com/video/av38088874/
?
喜馬拉雅語音筆記:
https://www.ximalaya.com/keji/19103006/144900713
轉(zhuǎn)載于:https://www.cnblogs.com/lhghroom/p/10125181.html
總結(jié)
以上是生活随笔為你收集整理的孤荷凌寒自学python第三十八天初识python的线程控制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 何为K-邻近算法
- 下一篇: Kotlin函数中默认参数