當前位置:
首頁 > 知識 > Python學習之進程和線程

Python學習之進程和線程


Linux編程

點擊右側關注,免費入門到精通!



作者丨stone_zhu
https://www.jianshu.com/p/4917e05dad63




對於操作系統來說,一個任務就是一個進程(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!




multiprocessing




如果你打算編寫多進程的服務程序,Unix/Linux無疑是正確的選擇。由於Windows沒有fork調用,難道在Windows上無法用Python編寫多進程的程序?




由於Python是跨平台的,自然也應該提供一個跨平台的多進程支持。

multiprocessing

模塊就是跨平台版本的多進程模塊。




multiprocessing

模塊提供了一個

Process

類來代表一個進程對象,下面的例子演示了啟動一個子進程並等待其結束:



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."

)




執行結果如下:



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.




Lock




多線程和多進程最大的不同在於,多進程中,同一個變數,各自有一份拷貝存在於每個進程中,互不影響,而多線程中,所有變數都由所有線程共享,所以,任何一個變數都可以被任何一個線程修改,因此,線程之間共享數據最大的危險在於多個線程同時改一個變數,把內容給改亂了。



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來確保鎖一定會被釋放。




ThreadLocal



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請求,用戶身份信息等,這樣一個線程的所有調用到的處理函數都可以非常方便地訪問這些資源。




 推薦↓↓↓ 






??

16個技術公眾號

】都在這裡!


涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與演算法、黑客技術和網路安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程序員等。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

第一次跟女朋友開房,怎麼都進不去

TAG:Python開發 |