做者博客:http://zzir.cnpython
進程是執行中的計算機程序。每一個進程都擁有本身的地址空間、內存、數據棧及其它的輔助數據。操做系統管理着全部的進程,併爲這些進程合理分配時間。進程能夠經過派生新的進程來執行其它任務,不過每一個進程都擁有本身的內存和數據棧等,進程之間的數據交換採用 進程間通訊(IPC) 方式。多線程
線程在進程之下執行,一個進程下能夠運行多個線程,它們之間共享相同上下文。線程包括開始、執行順序和結束三部分。它有一個指針,用於記錄當前運行的上下文。當其它線程執行時,它能夠被搶佔(中斷)和臨時掛起(也稱睡眠) ——這種作法叫作 讓步(yielding)。併發
一個進程中的各個線程與主進程共享同一片數據空間,與獨立進程相比,線程之間信息共享和通訊更加容易。線程通常以併發執行,正是因爲這種併發和數據共享機制,使多任務間的協做成爲可能。固然,這種共享也並非沒有風險的,若是多個線程訪問同一數據空間,因爲訪問順序不一樣,可能致使結果不一致,這種狀況一般稱爲競態條件(race condition),不過大多數線程庫都有同步原語,以容許線程管理器的控制執行和訪問;另外一個要注意的問題是,線程沒法給予公平執行時間,CPU 時間分配會傾向那些阻塞更少的函數。app
Python 代碼執行由 Python 虛擬機 (又名解釋器主循環) 進行控制。Python 在設計時是這樣考慮的,在主循環中同時只能有一個控制線程在執行。對 Python 虛擬機的訪問由 全局解釋器(GIL) 控制,這個鎖用於,當有多個線程時保證同一時刻只能有一個線程在運行。dom
因爲 Python 的 GIL 的限制,多線程更適合 I/O 密集型應用( I/O 釋放了 GIL,能夠容許更多的併發),對於計算密集型應用,爲了實現更好的並行性,適合使用多進程,已便利用 CPU 的多核優點。Python 的多進程相關模塊:subprocess、multiprocessing、concurrent.futureside
threading 是 Python 高級別的多線程模塊。函數
Lock.acquire()
, RLock.acquire()
, Condition.wait()
容許的最大值threading 可用對象列表:ui
Thread 對象的屬性有:Thread.name
、Thread.ident
、Thread.daemon
。詳見(The Python Standard Library)操作系統
Thread 對象方法:
Thread.start()
、Thread.run()
、Thread.join(timeout=None)
、Thread.getName
、Thread.setName
、Thread.is_alive()
、Thread.isDaemon()
、Thread.setDaemon()
。詳見(The Python Standard Library)線程
使用 Thread 類,能夠有不少種方法來建立線程,這裏使用常見的兩種:
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) # 打印 Sleep 的秒數 def main(): print("### Start at: ", ctime()) for i in range(10): hi(randint(1,2)) # 調用十次,每次 Sleep 1秒或2秒 print("### Done at: ", ctime()) if __name__ == '__main__': main()
運行結果:
### Start at: Thu Sep 1 14:11:00 2016 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ### Done at: Thu Sep 1 14:11:14 2016
一共是用了14秒。
直接上代碼:
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) # 實例化每一個 Thread 對象,把函數和參數傳遞進去,返回 Thread 實例 t = threading.Thread(target=hi, args=(rands,)) threads.append(t) # 分配線程 for i in range(10): threads[i].start() # 開始執行多線程 for i in range(10): threads[i].join() # (自旋鎖)等待線程結束或超時,而後再往下執行 print("### Done at: ", ctime()) if __name__ == '__main__': main()
運行結果:
### Start at: Thu Sep 1 14:18:00 2016 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 ### Done at: Thu Sep 1 14:18:02 2016
使用多線程,只用了2秒。
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime class MyThread(threading.Thread): def __init__(self, func, args, times): super(MyThread, self).__init__() self.func = func self.args = args self.times = times def run(self): print("begin thread......", self.times) self.res = self.func(*self.args) print("end threads......", self.times) def hi(n): sleep(n) print("ZzZzzz, sleep: ", n) def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) t = MyThread(hi, (rands,), i+1) threads.append(t) for i in range(10): threads[i].start() for i in range(10): threads[i].join() print("### Done at: ", ctime()) if __name__ == '__main__': main()
執行結果:
### Start at: Thu Sep 1 14:47:09 2016 begin thread...... 1 begin thread...... 2 begin thread...... 3 begin thread...... 4 begin thread...... 5 begin thread...... 6 begin thread...... 7 begin thread...... 8 begin thread...... 9 begin thread...... 10 ZzZzzz, sleep: 1 ZzZzzz, sleep: 1 end threads...... 1 end threads...... 4 ZzZzzz, sleep: 1 end threads...... 7 ZzZzzz, sleep: 1 end threads...... 3 ZzZzzz, sleep: 1 end threads...... 9 ZzZzzz, sleep: 2 end threads...... 2 ZzZzzz, sleep: 2 end threads...... 5 ZzZzzz, sleep: 2 ZzZzzz, sleep: 2 end threads...... 10 end threads...... 6 ZzZzzz, sleep: 2 end threads...... 8 ### Done at: Thu Sep 1 14:47:11 2016
這個栗子對 Thread 子類化,而不是對其實例化,使得定製線程對象更具靈活性,同時也簡化線程建立的調用過程。
當多線程爭奪鎖時,容許第一個得到鎖的線程進入臨街區,並執行代碼。全部以後到達的線程將被阻塞,直到第一個線程執行結束,退出臨街區,並釋放鎖。須要注意,那些阻塞的線程是沒有順序的。
舉個栗子:
#!/usr/bin/env python3 import threading from random import randint from time import sleep, ctime L = threading.Lock() # 引入鎖 def hi(n): L.acquire() # 加鎖 for i in [1,2]: print(i) sleep(n) print("ZzZzzz, sleep: ", n) L.release() # 釋放鎖 def main(): print("### Start at: ", ctime()) threads = [] for i in range(10): rands = randint(1,2) t = threading.Thread(target=hi, args=(rands,)) threads.append(t) for i in range(10): threads[i].start() for i in range(10): threads[i].join() print("### Done at: ", ctime()) if __name__ == '__main__': main()
運行上面的代碼,再將鎖的代碼註釋掉,對比下輸出。