Python學習之進程和線程
Linux編程
點擊右側關注,免費入門到精通!
作者丨stone_zhu
https://www.jianshu.com/p/4917e05dad63
對於操作系統來說,一個任務就是一個進程(Process),比如打開一個瀏覽器就是啟動一個瀏覽器進程,打開一個記事本就啟動了一個記事本進程,打開兩個記事本就啟動了兩個記事本進程,打開一個Word就啟動了一個Word進程。
有些進程還不止同時干一件事,比如Word,它可以同時進行打字、拼寫檢查、列印等事情。在一個進程內部,要同時干多件事,就需要同時運行多個「子任務」,我們把進程內的這些「子任務」稱為線程(Thread)。
進程
Python的os模塊封裝了常見的系統調用,其中包括fork,可以在Python程序中輕鬆創建子進程:
osimport
"Process (%s) start..."
%os
.getpid())
# Only works on Unix/Linux/Mac:
pid =
os
.fork()if
pid ==0
:"I am child process (%s) and my parent is %s."
% (os
.getpid(),os
.getppid()))else
:"I (%s) just created a child process (%s)."
% (
os
.getpid(), pid))運行結果如下:
876 childProcess (876) start...
I (
process (
877
).I am
child
process (877
)and
myparent
is
876.
由於Windows沒有
fork
調用,上面的代碼在Windows上無法運行。由於Mac系統是基於BSD(Unix的一種)內核,所以,在Mac下運行是沒有問題的,推薦大家用Mac學Python!
multiprocessing
如果你打算編寫多進程的服務程序,Unix/Linux無疑是正確的選擇。由於Windows沒有fork調用,難道在Windows上無法用Python編寫多進程的程序?
由於Python是跨平台的,自然也應該提供一個跨平台的多進程支持。
multiprocessing
模塊就是跨平台版本的多進程模塊。
multiprocessing
模塊提供了一個
Process
類來代表一個進程對象,下面的例子演示了啟動一個子進程並等待其結束:
from import import
# 子進程要執行的代碼
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."
)執行結果如下:
child test 929Parent process 928.
Process will start.
Run
Process end.
創建子進程時,只需要傳入一個執行函數和函數的參數,創建一個
Process
實例,用
start()
方法啟動,這樣創建進程比
fork()
還要簡單。
join()
方法可以等待子進程結束後再繼續往下運行,通常用於進程間的同步。
線程
Python的標準庫提供了兩個模塊:
_thread
和
threading
,
_thread
是低級模塊,
threading
是高級模塊,對
_thread
進行了封裝。絕大多數情況下,我們只需要使用
threading
這個高級模塊。
啟動一個線程就是把一個函數傳入並創建
Thread
實例,然後調用
start()
開始執行:
import
# 新線程執行的代碼:
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)執行結果如下:
>>> 1 >>> 2 >>> 3 >>> 4 >>> 5thread MainThread is running...
thread LoopThread is running...
thread LoopThread
thread LoopThread
thread LoopThread
thread LoopThread
thread LoopThread
thread LoopThread ended.
thread MainThread ended.
Lock
多線程和多進程最大的不同在於,多進程中,同一個變數,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變數都由所有線程共享,所以,任何一個變數都可以被任何一個線程修改,因此,線程之間共享數據最大的危險在於多個線程同時改一個變數,把內容給改亂了。
0balance =
lock = threading.Lock()
def
run_thread
(n)
:for
iin
range(100000
):# 先要獲取鎖:
lock.acquire()
try
:# 放心地改吧:
change_it(n)
finally
:# 改完了一定要釋放鎖:
lock.release()
當多個線程同時執行lock.acquire()時,只有一個線程能成功地獲取鎖,然後繼續執行代碼,其他線程就繼續等待直到獲得鎖為止。
獲得鎖的線程用完後一定要釋放鎖,否則那些苦苦等待鎖的線程將永遠等待下去,成為死線程。所以我們用try...finally來確保鎖一定會被釋放。
ThreadLocal
import
# 創建全局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()
執行結果:
in inHello, Alice (
Hello, Bob (
全局變數
local_school
就是一個
ThreadLocal
對象,每個
Thread
對它都可以讀寫
student
屬性,但互不影響。你可以把
local_school
看成全局變數,但每個屬性如
local_school.student
都是線程的局部變數,可以任意讀寫而互不干擾,也不用管理鎖的問題,
ThreadLocal
內部會處理。
可以理解為全局變數
local_school
是一個dict,不但可以用
local_school.student
,還可以綁定其他變數,如
local_school.teacher
等等。
ThreadLocal最常用的地方就是為每個線程綁定一個資料庫連接,HTTP請求,用戶身份信息等,這樣一個線程的所有調用到的處理函數都可以非常方便地訪問這些資源。
推薦↓↓↓
長
按
關
注
??
【
16個技術公眾號
】都在這裡!
涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與演算法、黑客技術和網路安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程序員等。
TAG:Python開發 |