最近,做了很多个报表,一开始并没有考虑到效率的问题,只是实现了excel的导出,后来慢慢做出现好多问题。
1、每一个excel 的sheet很多,大概四五十个,跑完一个excel得花四十多分钟,甚至一个小时。。。有时候甚至线程直接卡死了不动了。可能是io的交互太长了,也不知道撒原因。
2、每一类型的excel至少也得需要出30多个吧,考虑到进程池用多进程去跑,但是发现多进程消耗太大,机器也就4核的,我至少也得开十个进程,机器扛不住,可能也还是IO的交互时间长,进程也会卡死,所以放弃了。
3、为了解决进程池的开销太大,和进程卡死的问题,我改成了线程池,每一个类型的excel开启十个线程去跑,但是python的线程由于GIL全局锁的原因,实际上每次还是一个线程再跑,而且对每一个excel来说,很多个sheet需要生成,同一个线程中IO交互也非常频繁,而且还是会存在线程卡死的情况,效率也不行。
4、为了解决进程池的开销问题,和线程池中的io交互过长,会导致线程卡死,IO交互的时候线程会暂停等待IO交互而白白浪费时间。最终,goole、百度,python的协程,简直是神器,IO交互的时候,线程不会等待,而是会执行其他的协程。
5、因为我的是flask应用,需要向其他应用提供生成excel的接口,最终我的解决方案是,在每一个web请求中,创建进程池异步调用,并创建线程池(为了跑多个excel),每个线程开启多个协程任务(生成每个excel中的sheet)。大大提高了excel的生成效率,而且,线程也没与出现卡死的情况。下面是,threadpool 和 协程的基本使用:

1、每一个线程里面会有多个协程对象。
2、协程的运行是由顺序的,只是在IO交互的时候,不用等待IO交互完成。
3、多线程中使用协程的时候必须新建loop对象。

import threadpool
import asyncio
import time


async def do_work_one(name):
    """
    定义一个协程对象
    :return:
    """
    time.sleep(4)
    print(name, 'do_work_one')


async def do_work_two(name):
    """
    定义一个协程对象
    :return:
    """
    time.sleep(2)
    print(name, 'do_work_two')


def task_do_work(name):
    """
    1、每一个线程里面会有多个协程对象
    2、协程的运行是由顺序的,只是在IO交互的时候,不用等待IO交互完成
    3、多线程中使用协程的时候必须新建loop对象
    :return:
    """
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    coroutine1 = do_work_one(name)
    coroutine2 = do_work_two(name)

    tasks = [asyncio.ensure_future(coroutine1), asyncio.ensure_future(coroutine2)]
    loop.run_until_complete(asyncio.wait(tasks))
    return 'success'


def call_back(param, result):
    print('回调', param, result)


if __name__ == '__main__':
    jobs = []
    pool = threadpool.ThreadPool(2)
    work_requests = []
    for i in range(2):
        work_requests.append(threadpool.WorkRequest(task_do_work, args=('线程-{0}'.format(i), ), callback=call_back, exc_callback=call_back))
    [pool.putRequest(req) for req in work_requests]
    pool.wait()
    print('end')