Python 多線程 使用線程 (二)

來源:互聯網
上載者:User

標籤:pos   列表   返回   worker   運行時   無法自動   err   否則   rgs   

 

Python中實現多線程需要使用到 threading 庫,其中每一個 Thread類 的執行個體控制一個線程。

 

Thread類

#類簽名

def __init__(self, group=None, target=None, name=None,                 args=(), kwargs=None, *, daemon=None):

  簡單介紹一些初始化參數:

target: 指定線程由 run () 方法調用的可調用對象。預設為 None, 意味著不調用任何內容。

name: 指定該線程的名稱。 在預設情況下,建立一個唯一的名稱。

args: target調用的實參,元組格式。預設為 (),即不傳參。

daemon: 為False表示父線程在運行結束時需要等待子線程結束才能結束程式,為True則表示父線程在運行結束時,子線程無論是否還有任務未完成都會跟隨父進程退出,結束程式。

 

線程啟動:
import threadingdef worker(arg):#線程執行的目標函數    print("I‘m working {}".format(arg))    print("Fineshed")t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")#線程對象t.start()#啟動線程運行結果:I‘m working <_MainThread(MainThread, started 10936)>Fineshed

  上面例子中,當函數執行完之後,線程也就跟著退出了。

 

線程的傳參:
import threadingdef add(x,y):    print(x+y)t = threading.Thread(target=add,args=(4,5))t.start()print("====end===")運行結果:9====end===

  線程的傳參和函數傳參沒有區別,只需要注意傳入的必須為元祖格式。

 

線程退出:

如果線程中任務是無限迴圈語句,那這個線程將無法自動停止。

Python線程允出準則有以下幾種:

1、線程內的函數語句執行完畢,線程自動結束

2、線程內的函數拋出未處理的異常

import threadingimport timedef worker(arg):    while True:        time.sleep(1)        print("I‘m working {}".format(arg))    print("Fineshed")t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")t.start()運行結果:I‘m working <_MainThread(MainThread, stopped 2468)>I‘m working <_MainThread(MainThread, stopped 2468)>I‘m working <_MainThread(MainThread, stopped 2468)>...

  上面例子中,線程啟動後,將一直迴圈下去,線程不會自動結束。

import threadingimport timedef worker(arg):    count = 0    while True:        if count > 5:            raise RuntimeError(count)        time.sleep(1)        print("I‘m working {}".format(arg))        count += 1    print("Fineshed")t = threading.Thread(target=worker,args=(threading.enumerate(),))t.start()print("====end===")運行結果:====end===I‘m working [<_MainThread(MainThread, stopped 10992)>]I‘m working [<_MainThread(MainThread, stopped 10992)>]I‘m working [<_MainThread(MainThread, stopped 10992)>]I‘m working [<_MainThread(MainThread, stopped 10992)>]I‘m working [<_MainThread(MainThread, stopped 10992)>]I‘m working [<_MainThread(MainThread, stopped 10992)>]Exception in thread Thread-1:Traceback (most recent call last):  File "C:/python/test.py", line 8, in worker    raise RuntimeError(count)RuntimeError: 6

  上面例子中,示範了觸發異常自動結束線程。但最先列印的是主程式的"===end==="語句,是因為在程式中,主線程啟動一個線程後,不會等待子線程執行完畢,就繼續執行了後續語句,在執行完主線程語句後,發現還有子線程沒有結束,於是等待子線程執行結束,子線程在運行時拋出了未處理的異常,最終子線程結束,主線程也隨之結束。這裡需要瞭解daemon線程和non-daemon線程,稍後就會介紹。

 

threading屬性:
threading.current_thread()   返回當前線程對象
threading.main_thread() 返回主線程對象
threading.active_count() 返回處於Active狀態的線程個數
threading.enumerate() 返回所有存活的線程的列表,不包括已經終止的線程和未啟動的線程
threading.get_ident() 返回當前線程的ID,非0整數

舉例:

import threadingimport timedef showthreadinfo():    print("current thread = {}".format(threading.current_thread()))    print("main thread  = {}".format(threading.main_thread()))    print("active thread count = {}".format(threading.active_count()))    print("active thread list = {}".format(threading.enumerate()))    print("thread id = {}".format(threading.get_ident()))    print("~~~~~~~~~~~~~")def add(x,y):    time.sleep(1)    showthreadinfo() #子線程中調用    print(x+y)showthreadinfo() #主線程中調用time.sleep(1)t = threading.Thread(target=add,args=(4,5))t.start()print("====end===")運行結果:current thread = <_MainThread(MainThread, started 192)>main thread  = <_MainThread(MainThread, started 192)>active thread count = 1active thread list = [<_MainThread(MainThread, started 192)>]thread id = 192~~~~~~~~~~~~~====end===current thread = <Thread(Thread-1, started 8424)>main thread  = <_MainThread(MainThread, stopped 192)>active thread count = 2active thread list = [<_MainThread(MainThread, stopped 192)>, <Thread(Thread-1, started 8424)>]thread id = 8424~~~~~~~~~~~~~9

  上面例子中,在主線程中只能看到存活的只有自己,因為子線程還沒有啟動,且它的父線程就是它自己。子線程啟動時,它的名字為Thread-1,這個名字是解譯器自動命名的,如果定義線程對象時添加了name="threadName",則這裡顯示的就是threadName;同時,子線程的父線程就是主線程,也就是說誰啟動的線程誰就是它的父線程;子線程能看到的存活線程有父線程和自身。

 

Thread執行個體的屬性:
threading.current_thread().name        線程名,只是一個標識符,可以使用getName()、setName()擷取和運行時重新命名。
threading.current_thread().ident 線程ID,非0整數。線程啟動後才會有ID,否則為None。線程退出,此ID依舊可以訪問。此ID可以重複使用
threading.current_thread().is_alive() 返回線程是否存活,布爾值,True或False。

舉例:

import threadingimport timedef worker():    count = 1    while True:        if count >= 6:            break        time.sleep(1)        count += 1        print("thread name = {}".format(threading.current_thread().name))t = threading.Thread(target=worker,name="MyThread")t.start()while True:    time.sleep(1.1)    if t.is_alive():        print("{} {} alive".format(t.name,t.ident))    else:        print("{} {} alive".format(t.name, t.ident))        t.start()print("====end===")運行結果:thread name = MyThreadMyThread 9400 alivethread name = MyThreadMyThread 9400 alivethread name = MyThreadMyThread 9400 alivethread name = MyThreadMyThread 9400 alivethread name = MyThreadMyThread 9400 aliveTraceback (most recent call last):  File "C:/python/test.py", line 22, in <module>    t.start()    raise RuntimeError("threads can only be started once")RuntimeError: threads can only be started once

  從上面例子中可以看到子線程存活時的名字和線程ID,但線上程退出後,嘗試再次啟動線程時,拋出RuntimeError異常,表明線程對象在定義後只能啟動一次。

 

 舉例 getName()和setName():
import threadingimport timedef add(x,y):    for _ in range(5):        time.sleep(1)        print("x+y={}".format(x+y))t = threading.Thread(target=add,name="MyThread",args=(6,7))t.start()while True:    time.sleep(1)    if t.is_alive():        print("{} {} alive".format(t.name,t.ident))        print("Thread name",t.getName())        t.setName("MyThreadTwo")    else:        print("{} {} alive".format(t.name, t.ident))        print("Thread abort....")        break        # t.start()print("====end===")運行結果:MyThread 2564 aliveThread name MyThreadx+y=13MyThreadTwo 2564 aliveThread name MyThreadTwox+y=13MyThreadTwo 2564 aliveThread name MyThreadTwox+y=13MyThreadTwo 2564 aliveThread name MyThreadTwox+y=13MyThreadTwo 2564 aliveThread name MyThreadTwox+y=13MyThreadTwo 2564 aliveThread abort....====end===

  上面例子示範了在運行時擷取線程名和重新命名線程名。

 

線程的start()和run()方法:start():
import threadingimport timedef add(x,y):    for _ in range(5):        time.sleep(0.5)        print("x+y={}".format(x+y))class MyThread(threading.Thread):    def start(self):        print(‘start~~~~~~~~~~‘)        super().start()    def run(self):        print(‘run~~~~~~~~~~~~‘)        super().run()  #調用父類的start()和run()方法t = MyThread(target=add,name="MyThread",args=(6,7))t.start()# t.run()print("====end===")運行結果:start~~~~~~~~~~run~~~~~~~~~~~~====end===x+y=13x+y=13x+y=13x+y=13x+y=13

  從上面的例子中,可以看出start()方法會先運行start()方法,再運行run()方法。

跟進一下start() 方法源碼中的調用過程:

1、def start(self):    _start_new_thread(self._bootstrap, ())    ....2、_start_new_thread = _thread.start_new_thread3、def start_new_thread(function, args, kwargs=None):    pass4、def _bootstrap(self):    self._bootstrap_inner()5、def _bootstrap_inner(self):    ....    try:        self.run()#最終start()方法調用了run()方法    except SystemExit:        pass

  從上面跟蹤源碼的過程大概瞭解了start()方法如何調用到了run()方法。

 

run()方法:
import threadingimport timedef add(x,y):    for _ in range(5):        time.sleep(0.5)        print("x+y={}".format(x+y))class MyThread(threading.Thread):    def start(self):        print(‘start~~~~~~~~~~‘)        super().start()    def run(self):        print(‘run~~~~~~~~~~~~‘)        super().run()  #調用父類的start()和run()方法t = MyThread(target=add,name="MyThread",args=(6,7))# t.start()t.run()print("====end===")運行結果:run~~~~~~~~~~~~x+y=13x+y=13x+y=13x+y=13x+y=13====end===

  上面例子中,運行線程的run()方法只能調用到run()方法。

跟蹤一下run() 方法在源碼中的調用過程:

1、def __init__(self, group=None, target=None, name=None,                 args=(), kwargs=None, *, daemon=None):    self._target = target    self._args = args    self._kwargs = kwargs    ....2、def run(self):    if self._target:        self._target(*self._args, **self._kwargs)    ....

  可以看出,_target是我們傳入的目標函數,run()方法其實就類似一個裝飾器,最終還是將_args 和_kwargs 參數傳入目標函數運行,返回結果。

start() --> run() --> _target()

run() --> _target()

 

上面兩個例子簡單介紹了start()方法和run()方法的調用,下一篇文章再詳細看一下它們到底有什麼區別。

 

總結:

本文主要介紹了: Thread類、線程啟動、線程的傳參、線程退出、threading屬性、Thread執行個體的屬性、舉例getName()和setName()、線程的start()和run()方法

Python 多線程 使用線程 (二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.