装饰器实现多线程多协程并发编程

有时候写一些规模不算大得小脚本的时候,特意去实现多线程和协程的编程是一件繁杂的事情,在轻量级的爬虫中我们希望用同步编程的思想来实现异步的执行,下面给出一个初步的解决方案,通过装饰器装饰函数使请求函数可以接受一个可迭代的链接容器。转载请注明出处。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-08-14 20:52:16
# @Author  : Sheldon (thisisscret@qq.com)
# @blogs   : 谢耳朵的派森笔记
# @Link    : https://www.cnblogs.com/shld/

 

from gevent.pool import Group
from gevent import monkey

monkey.patch_all()
from concurrent.futures import ThreadPoolExecutor
from functools import partial, wraps
from multiprocessing import cpu_count


def eqlike_split(src, num):
    len_ = len(src)
    div = len_ // num
    mod = len_ % num
    sdx = 0
    splist = []
    for p_len in ([1] * mod + [0] * (num - mod)):
        edx = p_len + div + sdx
        splist.append(src[sdx:edx])
        sdx = edx
    return splist


def funcsplit(func, *args, **kw):
    """将args的首个元素分离出来,剩下的固定赋与为func函数的参数
    生成新的默认参数的函数,方便后续函数传参"""

    seed = args[0]
    new_args = args[1:]
    myfunc = partial(func, *new_args, **kw)
    return myfunc, seed


def aioreq(func, urlist, aionum=None, **kw):
    """gevent异步协程池执行func,返回response生成器"""

    igroup = Group()
    return igroup.imap_unordered(func, urlist, maxsize=aionum)


def thread_(func, seed, mtnum=2, **kw):
    """线程池池执行func,返回response生成器"""

    with ThreadPoolExecutor(mtnum) as executor:
        return executor.map(func, seed)


def mtaio(func, seed, mtnum=None, aionum=None, **kw):
    """多线程协程并发执行func,返回两层深度的response生成器"""

    spnum = mtnum
    if not mtnum:
        spnum = cpu_count()
    new_seed = eqlike_split(seed, spnum)
    myaioreq = partial(aioreq, func, aionum=aionum)
    return thread_(myaioreq, new_seed, mtnum)


def func2dec(decfunc=mtaio, mtnum=None, aionum=None):
    """使函数第一个参数接受变为参数迭代器的装饰器"""

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            fstuple = funcsplit(func, *args, **kw)
            return decfunc(*fstuple, mtnum=mtnum, aionum=aionum)

        return wrapper

    return decorator

 

用法:func2dec(decfunc=mtaio,mtnum=None,aionum=None)作为装饰器装饰请求函数就可以了,decfunc可选多线程、协程、多线程协程;mtnum使线程池大小,默认cpu个数;aionum是协程池大小,默认所有任务。

 

posted @ 2018-08-14 20:54  谢耳朵的派森笔记  阅读(1036)  评论(0编辑  收藏  举报