在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 進程
基礎(chǔ)
itertools
HTTP 服務
hashlib
閉包
文件和目錄
單元測試
使用 @property
標準模塊
陌生的 metaclass
Base64
進程、線程和協(xié)程
讀寫二進制文件
匿名函數(shù)
輸入和輸出
Click
元組
字符編碼
partial 函數(shù)
參考資料
collections
協(xié)程
類和實例
Python 之旅
定制類和魔法方法
常用數(shù)據(jù)類型
繼承和多態(tài)
ThreadLocal
HTTP 協(xié)議簡介
Requests 庫的使用
讀寫文本文件
列表
os 模塊
迭代器 (Iterator)
正則表達式
集合
上下文管理器
異常處理
你不知道的 super
定義函數(shù)
datetime
資源推薦
字典
slots 魔法
hmac
第三方模塊
進程
類方法和靜態(tài)方法
函數(shù)參數(shù)
高階函數(shù)
函數(shù)
re 模塊
高級特性
線程
argparse
生成器
結(jié)束語
字符串
map/reduce/filter
函數(shù)式編程
Celery
裝飾器

進程

進程(process)是正在運行的程序的實例,但一個程序可能會產(chǎn)生多個進程。比如,打開 Chrome 瀏覽器程序,它可能會產(chǎn)生多個進程,主程序需要一個進程,一個網(wǎng)頁標簽需要一個進程,一個插件也需要一個進程,等等。

每個進程都有自己的地址空間,內(nèi)存,數(shù)據(jù)棧以及其他記錄其運行狀態(tài)的輔助數(shù)據(jù),不同的進程只能使用消息隊列、共享內(nèi)存等進程間通訊(IPC)方法進行通信,而不能直接共享信息。

fork()

在介紹 Python 的進程編程之前,讓我們先看看 Unix/Linux 中的 fork 函數(shù)。在 Unix/Linux 系統(tǒng)中,fork 函數(shù)被用于創(chuàng)建進程。這個函數(shù)很特殊,對于普通的函數(shù),調(diào)用它一次,返回一次,但是調(diào)用 fork 一次,它返回兩次。事實上,fork 函數(shù)創(chuàng)建了新的進程,我們把它稱為子進程,子進程幾乎是當前進程(即父進程)的一個拷貝:它會復制父進程的代碼段,堆棧段和數(shù)據(jù)段。

對于父進程,fork 函數(shù)返回了子進程的進程號 pid,對于子進程,fork 函數(shù)則返回 0,這也是 fork 函數(shù)返回兩次的原因,根據(jù)返回值,我們可以判斷進程是父進程還是子進程。

下面我們看一段 C 代碼,它展示了 fork 的基本使用:

#include <unistd.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int pid;
    pid = fork();    // 使用 fork 函數(shù)

    if (pid < 0) {
        printf("Fail to create process\n");
    }
    else if (pid == 0) {
        printf("I am child process (%d) and my parent is (%d)\n", getpid(), getppid());
    }
    else {
        printf("I (%d) just created a child process (%d)\n", getpid(), pid);
    }
    return 0;
}

其中,getpid 用于獲取當前進程號,getppid 用于獲取父進程號。

事實上,Python 的 os 模塊包含了普遍的操作系統(tǒng)功能,該模塊也提供了 fork 函數(shù),把上面的代碼改成用 Python 來實現(xiàn),如下:

import os

pid = os.fork()

if pid < 0:
    print 'Fail to create process'
elif 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)

運行上面的代碼,產(chǎn)生如下輸出:

I (86645) just created a child process (86646).
I am child process (86646) and my parent is (86645).

需要注意的是,雖然子進程復制了父進程的代碼段和數(shù)據(jù)段等,但是一旦子進程開始運行,子進程和父進程就是相互獨立的,它們之間不再共享任何數(shù)據(jù)。

多進程

Python 提供了一個 multiprocessing 模塊,利用它,我們可以來編寫跨平臺的多進程程序,但需要注意的是 multiprocessing 在 Windows 和 Linux 平臺的不一致性:一樣的代碼在 Windows 和 Linux 下運行的結(jié)果可能不同。因為 Windows 的進程模型和 Linux 不一樣,Windows 下沒有 fork。

我們先來看一個簡單的例子,該例子演示了在主進程中啟動一個子進程,并等待其結(jié)束,代碼如下:

import os
from multiprocessing import Process

# 子進程要執(zhí)行的代碼
def child_proc(name):
    print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__ == '__main__':
    print 'Parent process %s.' % os.getpid()
    p = Process(target=child_proc, args=('test',))
    print 'Process will start.'
    p.start()
    p.join()
    print 'Process end.'

在上面的代碼中,我們從 multiprocessing 模塊引入了 Process,Process 是一個用于創(chuàng)建進程對象的類,其中,target 指定了進程要執(zhí)行的函數(shù),args 指定了參數(shù)。在創(chuàng)建了進程實例 p 之后,我們調(diào)用 start 方法開始執(zhí)行該子進程,接著,我們又調(diào)用了 join 方法,該方法用于阻塞子進程以外的所有進程(這里指父進程),當子進程執(zhí)行完畢后,父進程才會繼續(xù)執(zhí)行,它通常用于進程間的同步。

可以看到,用上面這種方式來創(chuàng)建進程比直接使用 fork 更簡單易懂。現(xiàn)在,讓我們看下輸出結(jié)果:

Parent process 7170.
Process will start.
Run child process test (10075)...
Process end.

multiprocessing 與平臺有關(guān)

import random
import os
from multiprocessing import Process

num = random.randint(0, 100)

def show_num():
    print("pid:{}, num is {}".format(os.getpid(), num))

if __name__ == "__main__":
    print("pid:{}, num is {}".format(os.getpid(), num))
    p = Process(target=show_num)
    p.start()
    p.join()

在 Windows 下運行以上代碼,輸出的結(jié)果如下(你得到不一樣的結(jié)果也是對的):

pid:6504, num is 25
pid:6880, num is 6

我們發(fā)現(xiàn),num 的值是不一樣的!

在 Linux 下運行以上代碼,可以看到 num 的值是一樣的:

pid:11747, num is 13
pid:11748, num is 13

使用進程池創(chuàng)建多個進程

在上面,我們只是創(chuàng)建了一個進程,如果要創(chuàng)建多個進程呢?Python 提供了進程池的方式,讓我們批量創(chuàng)建子進程,讓我們看一個簡單的示例:

import os, time
from multiprocessing import Pool

def foo(x):
    print 'Run task %s (pid:%s)...' % (x, os.getpid())
    time.sleep(2)
    print 'Task %s result is: %s' % (x, x * x)

if __name__ == '__main__':
    print 'Parent process %s.' % os.getpid()
    p = Pool(4)         # 設置進程數(shù)
    for i in range(5):
        p.apply_async(foo, args=(i,))    # 設置每個進程要執(zhí)行的函數(shù)和參數(shù)
    print 'Waiting for all subprocesses done...'
    p.close()
    p.join()
    print 'All subprocesses done.'

在上面的代碼中,Pool 用于生成進程池,對 Pool 對象調(diào)用 apply_async 方法可以使每個進程異步執(zhí)行任務,也就說不用等上一個任務執(zhí)行完才執(zhí)行下一個任務,close 方法用于關(guān)閉進程池,確保沒有新的進程加入,join 方法會等待所有子進程執(zhí)行完畢。

看看執(zhí)行結(jié)果:

Parent process 7170.
Run task 1 (pid:10320)...
Run task 0 (pid:10319)...
Run task 3 (pid:10322)...
Run task 2 (pid:10321)...
Waiting for all subprocesses done...
Task 1 result is: 1
Task 0 result is: 0
Run task 4 (pid:10320)...
Task 3 result is: 9
Task 2 result is: 4
Task 4 result is: 16
All subprocesses done.

進程間通信

進程間的通信可以通過管道(Pipe),隊列(Queue)等多種方式來實現(xiàn)。Python 的 multiprocessing 模塊封裝了底層的實現(xiàn)機制,讓我們可以很容易地實現(xiàn)進程間的通信。

下面以隊列(Queue)為例,在父進程中創(chuàng)建兩個子進程,一個往隊列寫數(shù)據(jù),一個從對列讀數(shù)據(jù),代碼如下:

# -*- coding: utf-8 -*-

from multiprocessing import Process, Queue

# 向隊列中寫入數(shù)據(jù)
def write_task(q):
    try:
        n = 1
        while n < 5:
            print "write, %d" % n
            q.put(n)
            time.sleep(1)
            n += 1
    except BaseException:
        print "write_task error"
    finally:
        print "write_task end"

# 從隊列讀取數(shù)據(jù)
def read_task(q):
    try:
        n = 1
        while n < 5:
            print "read, %d" % q.get()
            time.sleep(1)
            n += 1
    except BaseException:
        print "read_task error"
    finally:
        print "read_task end"

if __name__ == "__main__":
    q = Queue()  # 父進程創(chuàng)建Queue,并傳給各個子進程

    pw = Process(target=write_task, args=(q,))
    pr = Process(target=read_task, args=(q,))

    pw.start()   # 啟動子進程 pw,寫入
    pr.start()   # 啟動子進程 pr,讀取
    pw.join()    # 等待 pw 結(jié)束
    pr.join()    # 等待 pr 結(jié)束
    print "DONE"

執(zhí)行結(jié)果如下:

write, 1
read, 1
write, 2
read, 2
write, 3
read, 3
write, 4
read, 4
write_task end
read_task end
DONE

小結(jié)

  • 進程是正在運行的程序的實例。
  • 由于每個進程都有各自的內(nèi)存空間,數(shù)據(jù)棧等,所以只能使用進程間通訊(Inter-Process Communication, IPC),而不能直接共享信息。
  • Python 的 multiprocessing 模塊封裝了底層的實現(xiàn)機制,讓我們可以更簡單地編寫多進程程序。

參考資料

上一篇:異常處理下一篇:協(xié)程