1. 程式人生 > >孤荷凌寒自學python第三十八天初識python的執行緒控制

孤荷凌寒自學python第三十八天初識python的執行緒控制

 孤荷凌寒自學python第三十八天初識python的執行緒控制

 

(完整學習過程螢幕記錄視訊地址在文末,手寫筆記在文末)

一、執行緒

在作業系統中存在著很多的可執行的應用程式,每個應用程式啟動後,就可以看著是一個程序,當開啟WINDOWS工作管理員時,在工作管理員的程序選項卡中列出的就是一個一個的程序,基本上每個應用程式都對應著至少一個程序。

在同一程序中,也許同時在做著不止一件事情,比如在向程式介面上顯示資訊和接受資訊的同時,程式也在和遠端伺服器通訊讀取資料,則這兒至少有兩個執行緒執行在同一個程序中。

我的簡單理解是,在同一個作業系統的程序中,可以同時執行多個執行緒的任務。

 

二、threading模組

threading模組是Python 中主要的執行緒控制模組。

使用threading模組前必須先宣告引用:

import threading

 

三、threading模組的常見子類與屬性和方法

1

threading.activeCount()

執行此方法返回當前程序下所有活動的執行緒總數。

 

2

threading.enumerate()

此方法直接返回一個 迭代器,迭代器中包含了,所有當前程序下的所有執行緒的資訊。

在實際測試中,發現在迴圈列印時,總是不打印出第0個執行緒的資訊,沒有核準原因。

見後面的測試程式碼與結果。

 

3

threading.Thread

這是threading模組下的子模組(類),特別注意Thread模組的首字母是大寫的。這是最容易疏忽之處。

 

四、獲取或定義得到一個threading.Thread執行緒物件

可以通過以下方式得到threading.Thread執行緒物件

1.第一種:

thread物件=threading.Thread(target=要讓新執行緒執行的函式名 , args=這個函式需要的引數組成的元組)

測試程式碼:

def f1(n):

    strtime=str(datetime.now())

    print('執行緒' + n + '正在執行中....執行緒啟動於:' +strtime)

 

t=threading.Thread(target=f1,args=('t1',))

上面程式碼中,我建立了一個執行緒物件 t ,這個執行緒如果啟動,將執行函式f1定義好的內部程式碼塊。

Thread類的建構程式碼(初始化函式)中,要求傳遞的兩個實參,只能按【關鍵字引數】形式傳遞進去。

 

2.第二種

thread物件=threading.Thread(target=要呼叫的作為函式用的類名(此類的初始化方法需要的引數列表))

測試程式碼:

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啟動後,將執行的是一個可以當著函式來呼叫的類myt.

myt是一個可以被 當著函式來使用的類,因為在類的內部程式碼塊中定義有:__call__()私有方法。

與第一種方式不同的地方在於,這次只向Thread初始化方法傳遞了一個關鍵字實參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')

首先建構了一個繼自threading.Thread的類 myt,這個類的特點有:

(1)在子類的__init__()方法中,必須 先呼叫父類的__init__()方法,在我的上機測試過程中(見過程螢幕錄影,這兒反覆出錯,結果發現就是沒有先呼叫父類的__init()__方法,引發的錯誤。

而原因在測試當時沒有思考出來,所以大家在我的測試螢幕錄影中可以看到沒有想明白,現在思考下,發現可能是因為在threading.Thread類本身的__init__()中要執行非常重要的初始化操作,才能保證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)

 

以上程式碼可以證明我的猜測是正確的,因此在測試螢幕錄影中我沒有想通的問題現在基本上有答案了,當然正確與否希望大家批評指正。

(2)在定義繼承自threading.Thread類的子類中,__call__()方法 需要被 改名為 run()方法。

 

五、threading.Thread物件的主要屬性與方法有:

1 name屬性

此屬性可讀寫,用以設定和獲取執行緒物件的名稱。

 

2 getName()方法

用於獲取執行緒物件的名稱。

 

3 setName()方法

用於設定執行緒物件的名稱。

 

4 ident

此屬性是隻讀的,獲取執行緒物件的標識碼,標識碼是數字。

 

5 is_alive()  isAlive()

這兩個方法用於判斷執行緒物件是否正在執行(存活),返回布林物件。

 

6 join()

這是執行緒物件非常 重要 的方法,此方法有一個可選 形參 timeout

執行執行緒的此方法後,會將呼叫 此執行緒的上級執行緒 阻塞,然後等待此執行緒執行完成,如果為join()方法設定了timeout時間,那麼從此執行緒開始執行起計時,達到timeout指定的時間限額時,將自動解除對上級執行緒的阻塞。

在今天 的測試過程中,沒有著重測試此方法的具體功用,理解可能不完全準確。

 

7 start()

執行緒執行此方法後,執行緒就開始執行指定任務,同時執行緒的isAlive標識將顯示為True

此外,我沒有發現執行緒有stop()方法,目前也沒有研究到中止指定執行緒執行的其它方法。

 

測試程式碼一 :

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

執行結果:

執行緒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()

執行結果:

執行緒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

注意執行結果中,threading.enumerate()方法返回的迭代器在迴圈取出物件時,總沒有完整列印,且有空行。這點沒有思考出答案。搜尋網路沒有找到答案,還有待繼續思考,並懇請高手予以指點。

 

——————————

今天整理的學習筆記完成,最後例行說明下我的自學思路:

根據過去多年我自學各種程式語言的經歷,認為只有真正體驗式,解決實際問題式的學習才會有真正的效果,即讓學習實際發生。在2004年的時候我開始在一個鄉村小學自學電腦 並學習vb6程式語言,沒有學習同伴,也沒有高師在上,甚至電腦都是孤島(鄉村那時還沒有網路),有的只是一本舊書,在痛苦的自學摸索中,我找到適應自己零基礎的學習方法:首先是每讀書的一小節就作相應的手寫筆記,第二步就是上機測試每一個筆記內容是否實現,其中會發現書中講的其實有出入或錯誤,第三步就是在上機測試之後,將筆記改為電子版,形成最終的修訂好的正確無誤的學習筆記。

通過反覆嘗試錯誤,在那個沒有分享與交流的黑暗時期我摸黑學會了VB6,爾後接觸了其它語言,也曾聽過付費視訊課程,結果發現也許自己學歷果然太低,就算是零基礎的入門課程,其實也難以跟上進度,講師的教學多數出現對初學者的實際情況並不瞭解的情況,況且學習者的個體也存在差異呢?當然更可怕的是收費課程的價格往往是自己難以承受的。

於是我的所有程式設計學習都改為了自學,繼續自己的三步學習筆記法的學習之路。

當然自學的最大問題是會走那麼多的彎路,沒有導師直接輸入式的教學來得直接,好在網路給我們帶來無限搜尋的機會,大家在網路上的學習日誌帶給我們共享交流的機會,而QQ群等交流平臺、網路社群的成立,我們可以一起自學,互相批評交流,也可以獲得更有效,更自主的自學成果。

於是我以人生已過半的年齡,決定繼續我的程式設計自學之路,開始學習python,只希望與大家共同交流,一個人的獨行是可怕的,只有一群人的共同前進才是有希望的。

誠摯期待您的交流分享批評指點!歡迎聯絡我加入從零開始的自學聯盟。

這個時代網際網路成為了一種基礎設施的存在,於是本來在孤獨學習之路上的我們變得不再孤獨,因為網路就是一個新的客廳,我們時刻都可以進行沙龍活動。

非常樂意能與大家一起交流自己自學心得和發現,更希望大家能夠對我學習過程中的錯誤給予指點——是的,這樣我就能有許多免費的高師了——這也是分享時代,社群時代帶來的好福利,我相信大家會的,是吧!

 

根據完全共享的精神,開源互助的理念,我的個人自學錄製過程是全部按4K高清視訊錄製的,從手寫筆記到驗證手寫筆記的上機操作過程全程錄製,但因為4K高清檔案太大均超過5G以上,所以無法上傳至網路,如有需要可聯絡我QQ578652607對傳,樂意分享。上傳分享到百度網盤的只是壓縮後的720P的視訊。

 

我的學習過程錄影百度盤地址分享如下:(清晰度:1280x720)

連結:https://pan.baidu.com/s/1n00vs2zpHiMCCf5eqykGrg 

提取碼:bgu2

 

Bilibili:

https://www.bilibili.com/video/av38088874/

 

喜馬拉雅語音筆記:

https://www.ximalaya.com/keji/19103006/144900713

圖片

圖片

圖片