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 threading
def 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 threading
def add(x,y):
print(x+y)
t = threading.Thread(target=add,args=(4,5))
t.start()
print("====end===")
運行結果:
9
====end===
線程的傳參和函數傳參沒有區別,只需要注意傳入的必須為元祖格式。
線程退出:
如果線程中任務是無限循環語句,那這個線程將無法自動停止。
Python線程退出條件有以下幾種:
1、線程內的函數語句執行完畢,線程自動結束
2、線程內的函數拋出未處理的異常
import threading
import time
def 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 threading
import time
def 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 threading
import time
def 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 = 1
active 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 = 2
active 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 threading
import time
def 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 = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
Traceback (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 threading
import time
def 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 alive
Thread name MyThread
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread abort....
====end===
上面例子演示了在運行時獲取線程名和重命名線程名。
線程的start()和run()方法:
start():
import threading
import time
def 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=13
x+y=13
x+y=13
x+y=13
x+y=13
從上面的例子中,可以看出start()方法會先運行start()方法,再運行run()方法。
跟進一下start() 方法源碼中的調用過程:
1、def start(self): _start_new_thread(self._bootstrap, ()) .... 2、_start_new_thread = _thread.start_new_thread 3、def start_new_thread(function, args, kwargs=None): pass 4、def _bootstrap(self): self._bootstrap_inner() 5、def _bootstrap_inner(self): .... try: self.run()#最終start()方法調用了run()方法 except SystemExit: pass
從上面跟蹤源碼的過程大概了解了start()方法如何調用到了run()方法。
run()方法:
import threading
import time
def 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=13
x+y=13
x+y=13
x+y=13
x+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()方法
