1. 程式人生 > >Python 多程序池的學習

Python 多程序池的學習

最近想用python寫個爬蟲,根據學校圖書館網站的學號來破解密碼。由於學校圖書館初始密碼為6位數字,而且不需要驗證碼,所以破解起來很簡單。思路就是生成6為數字暴力密碼本,依次向網頁POST“學號-密碼”的表單即可。然後問題來了,6位數字組成的密碼,也就是0-9的六位全排列,共1000000中排列。 短短40行的單程序程式碼,需要56個小時才能把1000000個暴力密碼輪詢完畢。可怕。。。。   以前學習的時候,多執行緒和多程序的非阻塞式程式設計淺嘗輒止,真是書到用時方恨少。

由於Python語言的“多執行緒”並不能提高效率,所以在這裡溫習一下“多程序”的用法:

python的用多程序池來管理所有的程序,步驟如下:

1)例項化程序池 p=Pool()。

2)通過apply_async()函式向程序池中新增程序。

3)通過p.close()函式關閉程序池,關閉之後無法繼續新增新的程序。與此同時,CPU開始從程序池中取程序,若CPU有N核,則CPU每次只會取N個程序,待N個程序全部

執行完畢,再取出N個。直到將程序池中的程序取完為止。

4)通過p.join()函式等待所有程序被取出並執行完畢。

示例程式碼如下:

import time
from multiprocessing import Pool


def long_time_task(name):
    print 'task %s starts running' % name
    time.sleep(3)
    print 'task %s ends running --3 seconds' % name

if __name__ =='__main__':
    start = time.time()
    p = Pool()
    for i in range(4):  # CPU有幾核,每次就取出幾個程序
        p.apply_async(func=long_time_task, args=(i,))
    p.close()   # 呼叫join()之前必須先呼叫close(),呼叫close()之後就不能繼續新增新的Process了
    p.join()    # 對Pool物件呼叫join()方法會等待所有子程序執行完畢
    end = time.time()
    print('多程序(非阻塞)執行共需時間為:%.2f' % (end-start))

執行結果如下:

task 0 starts running
task 1 starts running
task 2 starts running
task 3 starts running
task 2 ends running --3 seconds
task 0 ends running --3 seconds
task 1 ends running --3 seconds
task 3 ends running --3 seconds
多程序(非阻塞)執行共需時間為:3.08
可以看出,我的CPU是4核,所以CPU每次取4個程序,同時執行,每個程序單獨執行的時間是3秒(非阻塞式執行需要3*4=12秒),但同時執行後所用的實際時間為3.08秒。

然後我們把:

for i in range(4):

改成:

for i in range(5):
重新執行,結果如下:
task 0 starts running
task 1 starts running
task 2 starts running
task 3 starts running
task 0 ends running --3 seconds
task 4 starts running
task 1 ends running --3 seconds
task 2 ends running --3 seconds
task 3 ends running --3 seconds
task 4 ends running --3 seconds
多程序(非阻塞)執行共需時間為:6.08
可以看出增加了一個程序,總時間卻增加了3秒。這是因為CPU先取出0-3號程序,執行完畢後,4號程序才開始執行。0-3號程序花了3秒鐘,4號 程序也花了3秒。其實,如果我們新增8個程序,執行時間也是6秒左右。

-------------------------------------------------------------------------------------------------我是分割線-----------------------------------------------------------------------------------------------------------------------下面我們來研究一下apply_async()這個函式:

我們只用到兩個引數func和args,前者傳入函式名,後者傳入引數值。

如果我們這樣修改程式碼:把傳入的函式名放在Tasks類中

import time
from multiprocessing import Pool

class Tasks():
    def long_time_task(name):
        print 'task %s starts running' % name
        time.sleep(3)
        print 'task %s ends running --3 seconds' % name

def long_time_task(name):
    print 'task %s starts running' % name
    time.sleep(3)
    print 'task %s ends running --3 seconds' % name

if __name__ =='__main__':
    task = Tasks()

    start = time.time()
    p = Pool()
    for i in range(8):  # CPU有幾核,每次就取出幾個程序
        p.apply_async(func=task.long_time_task, args=(i,))
    p.close()   # 呼叫join()之前必須先呼叫close(),呼叫close()之後就不能繼續新增新的Process了
    p.join()    # 對Pool物件呼叫join()方法會等待所有子程序執行完畢
    end = time.time()
    print('多程序(非阻塞)執行共需時間為:%.2f' % (end-start))

    print type(task.long_time_task)
    print type(long_time_task)

我們執行之後,發現子程序並沒有執行啊!! 這是怎麼回事???我們先看下控制檯:
多程序(非阻塞)執行共需時間為:0.14
<type 'instancemethod'>
<type 'function'>
可以看到 task.long_time_task的型別是‘instancemethod’(例項方法),而long_time_task的型別是'function'(函式),型別不一樣,有可能是問題的根源哦。

然後繼續修改程式,講Tasks類中的long_time_task改成靜態方法行不行呢?我們來試試看:

修改後的程式碼如下:

import time
from multiprocessing import Pool

class Tasks():
    @staticmethod
    def long_time_task(name):
        print 'task %s starts running' % name
        time.sleep(3)
        print 'task %s ends running --3 seconds' % name

def long_time_task(name):
    print 'task %s starts running' % name
    time.sleep(3)
    print 'task %s ends running --3 seconds' % name

if __name__ =='__main__':
    start = time.time()
    p = Pool()
    for i in range(8):  # CPU有幾核,每次就取出幾個程序
        p.apply_async(func=Tasks.long_time_task, args=(i,))
    p.close()   # 呼叫join()之前必須先呼叫close(),呼叫close()之後就不能繼續新增新的Process了
    p.join()    # 對Pool物件呼叫join()方法會等待所有子程序執行完畢
    end = time.time()
    print('多程序(非阻塞)執行共需時間為:%.2f' % (end-start))

    print type(Tasks.long_time_task)
    print type(long_time_task)

控制檯輸出如下:
多程序(非阻塞)執行共需時間為:0.18
<type 'function'>
<type 'function'>
說明什麼? 說明雖然靜態方法的型別是'function‘,但程序還是沒有被執行嗎?  這不就麻煩了嗎,我就想把多程序封裝到類裡咋辦呢??暫時還沒弄明白,等弄明白之後在回來填坑,不過現在發現可以通過“類外部的函式包裹類內靜態函式”,這樣的包裹方法也可以讓子程序執行。程式碼如下:
import time
from multiprocessing import Pool

class Tasks():
    @staticmethod
    def long_time_task(name):
        print 'task %s starts running' % name
        time.sleep(3)
        print 'task %s ends running --3 seconds' % name


def long_time_task(name):
    Tasks.long_time_task(name)

if __name__ =='__main__':
    start = time.time()
    p = Pool()
    for i in range(8):  # CPU有幾核,每次就取出幾個程序
        p.apply_async(func=long_time_task, args=(i,))
    p.close()   # 呼叫join()之前必須先呼叫close(),呼叫close()之後就不能繼續新增新的Process了
    p.join()    # 對Pool物件呼叫join()方法會等待所有子程序執行完畢
    end = time.time()
    print('多程序(非阻塞)執行共需時間為:%.2f' % (end-start))

    print type(Tasks.long_time_task)
    print type(long_time_task)