Python學習目錄python
對於操做系統來講,一個任務就是一個進程(Process),好比打開一個瀏覽器就是啓動一個瀏覽器進程,打開一個記事本就啓動了一個記事本進程,打開兩個記事本就啓動了兩個記事本進程,打開一個Word就啓動了一個Word進程。數據庫
有些進程還不止同時幹一件事,好比Word,它能夠同時進行打字、拼寫檢查、打印等事情。在一個進程內部,要同時幹多件事,就須要同時運行多個「子任務」,咱們把進程內的這些「子任務」稱爲線程(Thread)。編程
Python的os
模塊封裝了常見的系統調用,其中包括fork
,能夠在Python程序中輕鬆建立子進程:瀏覽器
import os
print('Process (%s) start...' % os.getpid())
# Only works on Unix/Linux/Mac:
pid = os.fork()
if pid == 0:
print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid()))
else:
print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
複製代碼
運行結果以下:網絡
Process (876) start...
I (876) just created a child process (877).
I am child process (877) and my parent is 876.
複製代碼
因爲Windows沒有fork
調用,上面的代碼在Windows上沒法運行。因爲Mac系統是基於BSD(Unix的一種)內核,因此,在Mac下運行是沒有問題的,推薦你們用Mac學Python!多線程
若是你打算編寫多進程的服務程序,Unix/Linux無疑是正確的選擇。因爲Windows沒有fork
調用,難道在Windows上沒法用Python編寫多進程的程序?函數式編程
因爲Python是跨平臺的,天然也應該提供一個跨平臺的多進程支持。multiprocessing
模塊就是跨平臺版本的多進程模塊。函數
multiprocessing
模塊提供了一個Process
類來表明一個進程對象,下面的例子演示了啓動一個子進程並等待其結束:oop
from multiprocessing import Process
import os
# 子進程要執行的代碼
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
複製代碼
執行結果以下:post
Parent process 928.
Process will start.
Run child process test (929)...
Process end.
複製代碼
建立子進程時,只須要傳入一個執行函數和函數的參數,建立一個Process
實例,用start()
方法啓動,這樣建立進程比fork()
還要簡單。
join()
方法能夠等待子進程結束後再繼續往下運行,一般用於進程間的同步。
Python的標準庫提供了兩個模塊:_thread
和threading
,_thread
是低級模塊,threading
是高級模塊,對_thread
進行了封裝。絕大多數狀況下,咱們只須要使用threading
這個高級模塊。
啓動一個線程就是把一個函數傳入並建立Thread
實例,而後調用start()
開始執行:
import time, threading
# 新線程執行的代碼:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
複製代碼
執行結果以下:
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.
複製代碼
多線程和多進程最大的不一樣在於,多進程中,同一個變量,各自有一份拷貝存在於每一個進程中,互不影響,而多線程中,全部變量都由全部線程共享,因此,任何一個變量均可以被任何一個線程修改,所以,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂了。
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要獲取鎖:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了必定要釋放鎖:
lock.release()
複製代碼
當多個線程同時執行lock.acquire()
時,只有一個線程能成功地獲取鎖,而後繼續執行代碼,其餘線程就繼續等待直到得到鎖爲止。
得到鎖的線程用完後必定要釋放鎖,不然那些苦苦等待鎖的線程將永遠等待下去,成爲死線程。因此咱們用try...finally
來確保鎖必定會被釋放。
import threading
# 建立全局ThreadLocal對象:
local_school = threading.local()
def process_student():
# 獲取當前線程關聯的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 綁定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
複製代碼
執行結果:
Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)
複製代碼
全局變量local_school
就是一個ThreadLocal
對象,每一個Thread
對它均可以讀寫student
屬性,但互不影響。你能夠把local_school
當作全局變量,但每一個屬性如local_school.student
都是線程的局部變量,能夠任意讀寫而互不干擾,也不用管理鎖的問題,ThreadLocal
內部會處理。
能夠理解爲全局變量local_school
是一個dict
,不但能夠用local_school.student
,還能夠綁定其餘變量,如local_school.teacher
等等。
ThreadLocal
最經常使用的地方就是爲每一個線程綁定一個數據庫鏈接,HTTP請求,用戶身份信息等,這樣一個線程的全部調用到的處理函數均可以很是方便地訪問這些資源。