1. 程式人生 > >python 64式: 第4式、eventlet協程實現併發

python 64式: 第4式、eventlet協程實現併發

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from datetime import datetime

import eventlet


eventlet.monkey_patch(all=True)

from eventlet.green import urllib2

'''
關鍵:
1 eventlet:協程(綠色執行緒),一個執行緒內的偽併發方式
原理:是執行序列,有獨立棧和區域性變數,與其他協程共享全域性變數
與執行緒區別:多個執行緒可同時執行,而同一時間內,只允許一個協程執行,所以無需考慮鎖
好處:協程執行順序由程式決定,若工作耗時,協程會讓出CPU,充分利用CPU效能。非阻塞。
本質:協程休息時會儲存當前暫存器,重新工作會恢復
適用: 一個用來處理和網路相關的python庫函式,而且可以通過協程來實現併發

參考文件:
https://www.cnblogs.com/Security-Darren/p/4170031.html

2 綠化與猴子補丁
所謂”綠化”是指綠化後的Python環境支援綠色執行緒的執行模式。
Python原生的標準庫不支援eventlet這種綠色執行緒之間互相讓渡CPU控制權的執行模型,
為此eventlet開發者改寫了部分Python標準庫(自稱”補丁“)。
如果想在應用中使用eventlet,需要顯式地綠化自己要引入的模組。

猴子補丁,在執行時動態修改已有程式碼,而無需修改原始程式碼
使用:替換標準庫中的元件,例如socket
用法
import eventlet
eventlet.monkey_patch()   # 預設不加任何引數的情況下,是所有可能的module都會被patch。
monkey_patch(all=True,os=None, select=None, 
socket=None,thread=None,time=None,psycopg=None)

3 urllib2:用於抓取網頁並儲存到本地。把URL做為請求被髮送到服務端,讀取
服務端的響應資源
urllib2.urlopen(url,data,timeout)
返回值:instance
data是傳送給服務端的內容
參考: https://docs.python.org/2/library/urllib2.html

4 eventlet.GreenPool()
作用:提供了對greenthread支援,提供已定數量的綠色執行緒,實際
就是綠色執行緒池
class eventlet.greenpool.GreenPool(size=1000) 
imap(function,* iterables)
等同於: itertools.imap(function, **iterable),在併發
和記憶體使用上等同於starmap()
用於:處理檔案

5  eventlet.GreenPile(size_or+pool=1000)
GreenPile:用於表示抽象的IO相關任務
spawn(func, *args, **kw):
執行func在自己的綠色執行緒中,結果可以通過迭代
綠色管道
參考:http://eventlet.net/doc/modules/greenpool.html#eventlet.greenpool.GreenPool.spawn_n

6 不論是gevent還是eventlet,因為不存在實際的併發,響應時間和沒有併發區別不大。
所以所謂的併發應該是看
如果用了monkey_patch替換的模組換成併發的模組,才會帶來這個影響

總結:
eventlet是通過將標準python模組替換為可併發的模組來實現併發
'''

URLS=["https://www.baidu.com/"]

def timeHelper(func):
    def wrapper(*args, **kwargs):
        try:
            start = datetime.now()
            info = "############ Start at: %s ##########" % (
                str(start))
            print info
            result = func(*args, **kwargs)
            end = datetime.now()
            diff = end - start
            info = "##### End at: %s, cost time: %s #####" % (
                str(end), str(diff))
            print info
            return result
        except Exception as ex:
            info = "Exception type is %s, message is %s" % (ex.__class__.__name__, ex)
            print info
    return wrapper

def getUrl(url):
    stream = urllib2.urlopen(url)
    result = stream.read()
    return result

@timeHelper
def batch(urls, batchNum):
    i = 0
    results = []
    tempUrls = []
    for url in urls:
        tempUrls.append(url)
        if (i + 1) % batchNum == 0:
            result = validGreenPile(tempUrls)
            results.extend(result)
            tempUrls = []
        i += 1
    if tempUrls:
        result = validGreenPile(tempUrls)
        results.extend(result)
    return results


@timeHelper
def batchPool(urls, batchNum):
    i = 0
    results = []
    tempUrls = []
    for url in urls:
        tempUrls.append(url)
        if (i + 1) % batchNum == 0:
            result = validGreenPool(tempUrls)
            results.extend(result)
            tempUrls = []
        i += 1
    if tempUrls:
        result = validGreenPool(tempUrls)
        results.extend(result)
    return results


def validGreenPile(urls):
    pool = eventlet.GreenPool()
    pile = eventlet.GreenPile(pool)
    for url in urls:
        pile.spawn(getUrl, url)
    results = []
    for pl in pile:
        results.append(pl)
    return results


def validGreenPool(urls):
    pool = eventlet.GreenPool()
    results = []
    for result in pool.imap(getUrl, urls):
        results.append(result)
    return results


def getUrls(times):
    urls = []
    for i in range(times):
        urls.extend(URLS)
    return urls


def process():
    urls = getUrls(200)
    batchPool(urls, 100)

if __name__ == "__main__":
    process()