Python 多線程Threading
不喜歡廢話,先上今天的代碼!
#-*- coding:utf-8 -*- import threading class MyThreading(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): print self.num t = MyThreading(100) t.start() print t.isAlive()
乍一看,很簡單的threading代碼。首先我們繼承了Thread類,在子類里初始化后又重寫了run方法。最后我們實例化MyThreading子類,然后打印我們的num參數。最后再打印線程執行狀態。
初學者一看,絕對分為2派:1、支持說先打印num參數,然后才打印線程狀態。 2、拍胸脯保證先打印線程狀態再打印num參數;
其實結果出人預料:
結果1:
C:\Python27\python.exe D:/ProjectSpace/thread-example.py 100True Process finished with exit code 0
結果2:
C:\Python27\python.exe D:/ProjectSpace/thread-example.py True100 Process finished with exit code 0
吃驚嗎?也許每次運行結果可能會有幾次相同,但是在運行10次內,至少會有1-4次不同結果。
答案:線程的執行將是無序的
帶著上面的問題,我們繼續看下面一個例子:
#-*- coding:utf-8 -*- import threading,time class MyThreading(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self): time.sleep(10) print self.num def function1(): t1.start() time.sleep(1) print "t1 thread was Destruction " def function2(): t2.start() time.sleep(1) print "t2 thread was Destruction " t1 = MyThreading(10) t2 = MyThreading(20) t2.setDaemon(True) function1() function2()
當你多次運行代碼的時候,大概你能看到如下結果:
C:\Python27\python.exe D:/ProjectSpace/thread-example.py t1 thread was Destruction t2 thread was Destruction 10 Process finished with exit code 0
讓我們看看單步調試運行下的結果是怎么樣的:
C:\Python27\python.exe "C:\Program Files (x86)\JetBrains\PyCharm 5.0.3\helpers\pydev\pydevd.py" --multiproc --qt-support --client 127.0.0.1 --port 62267 --file D:/ProjectSpace/thread-example.py Connected to pydev debugger (build 143.1559) 10 t1 thread was Destruction 20 t2 thread was Destruction Process finished with exit code 0
是的, 你沒有看錯! 出現不同的結果的原因是:
Threadclass.setDeamon方法指定了一個線程為主線程;而主線程的作用是當它要退出時會先檢查子進程是否完成,如果未完成則繼續等待,完成則退出。
這也是為什么我們在Debug模式時走的是邏輯上的方法,而實際上在運行時的結果卻如上所述。
那么問題來了!:我們如何保證程序的順序執行呢?
為了保證線程的執行順序或者說是為了保證一致性,針對線程的操作引入了同步的概念。而針對同步最好的辦法也就是對操作進行加鎖了。(加鎖讓我想起了MySQL的鎖 - _ -)
請繼續看下面的2個例子:
例1:
#-*- coding:utf-8 -*- import threading,time ####繼承Thread類 class MyThreading(threading.Thread): def __init__(self,name): ####初始化父類 threading.Thread.__init__(self,name=name) def run(self): ####引入全局變量 global x ####給操作加鎖 lock.acquire() for i in range(3): x = x + 1 time.sleep(3) print x ####操作完成并解鎖 lock.release() ####實例化一個鎖的類 lock = threading.RLock() ####定義一個空的線程類的列表 tl = [] ####實例化10個類,并且將類添加到tl這個類列表里 for i in range(10): t = MyThreading(str(i)) tl.append(t) ####初始化全局 x = 0 ####逐個執行所有已經實例化的線程 for i in tl: i.start()
輸出結果如下
C:\Python27\python.exe D:/ProjectSpace/thread-example.py 3 6 9 12 15 18 21 24 27 30 Process finished with exit code 0
代碼很簡單,首先通過一個循環實例化10個類放進一個列表內,再從列表內遍歷出來運行。 因為執行了加鎖——》釋放這個步驟。如果在time.sleep()時間內未到,release無法執行。所以我們能控制程序的順序。
例2:
#-*- coding:utf-8 -*- import threading,time ####繼承Thread類 class MyThreading(threading.Thread): def __init__(self,name): ####初始化父類 threading.Thread.__init__(self,name=name) def run(self): ####引入全局變量 global x ####給操作加鎖 #lock.acquire() for i in range(3): x = x + 1 time.sleep(3) print x ####操作完成解鎖 #lock.release() ####實例化一個鎖的類 #lock = threading.RLock() ####定義一個空的線程類的列表 tl = [] ####實例化10個類,并且將類添加到tl這個類列表里 for i in range(10): t = MyThreading(str(i)) tl.append(t) ####初始化全局 x = 0 ####逐個執行所有已經實例化的線程 for i in tl: i.start()
輸出結果如下
C:\Python27\python.exe D:/ProjectSpace/thread-example.py 30 30 30 30 30 30 30 30 30 30 Process finished with exit code 0
同例1一樣,程序從列表拿出實例化的類運行;但是因為沒對run方法進行加鎖,所以線程運行到time.sleep()時不會等待就啟動第二個....第三個.....直到最后一個。
而當run方法全部運行完畢后,并且計算出全局變量X的最終值,time.sleep時間到了;這時候X已經為30,所以每個X輸出的值都是30了。
一個簡單的Threading函數同步實例就說到這里! 下次見~~~ (— 3 —)