Python并发与并行编程

概念解释

并发

并发(concurrency)指计算机似乎同时在做很多事情。例如对于单CPU机器,操作系统会在各个程序之间进行快速的切换,达到多个程序同时执行的假象。

并行

并行(Parallel)系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行。

区别

并发,指的是多个事情,在同一时间段内同时发生了。
并行,指的是多个事情,在同一时间点上同时发生了。

并发的多个任务之间是互相抢占资源的。
并行的多个任务之间是不互相抢占资源的、

只有在多CPU的情况中,才会发生并行。否则,看似同时发生的事情,其实都是并发执行的。
由于真实世界CPU数量是有限的,但是任务是无限的,因此我们一般说高并发,而不说高并行。

并发编程

在python的原始解释器CPython中存在着GIL(Global Interpreter Lock,全局解释器锁),这就导致了虽然是多线程的,但是一个时间只有一个线程在占用解释器。
因此Python中的多线程是真实的并发,即使拥有多个CPU也没法充分利用。因此仅仅适用于IO密集型任务,对于CPU密集型不适用。

并行编程

对于CPU密集型工作,可以通过concurrent.futures库提供的ProcessPoolExecutor类来实现。
一个ProcessPoolExecutor创建N个独立的Python解释器, N是系统上面可用CPU的个数。你可以通过提供可选参数给ProcessPoolExecutor(N)来修改处理器数量。

编程实现

import time
import logging
import concurrent.futures
from multiprocessing import cpu_count

logging.basicConfig(level=logging.WARNING,
                    format="%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s")
logger = logging.getLogger("local")


class Executor(object):
    def __init__(self, worker_num):
        raise NotImplementedError()

    def execute(self, func, batch_args):
        raise NotImplementedError()

    def _get_sorted_ret(self, ret_list):
        sorted_ret_list = [ret[1] for ret in sorted(ret_list)]
        return sorted_ret_list

    @classmethod
    def create(cls, config):
        worker_num = config.get("worker_num")
        return cls(worker_num)


class ParallelProcessExecutor(Executor):
    """多进程并行, 计算密集适用"""

    def __init__(self, worker_num=None):
        self.proc_num = cpu_count() if worker_num is None else min(worker_num, cpu_count())
        self.process_pool = concurrent.futures.ProcessPoolExecutor(max_workers=cpu_count())
        logger.info("Initialize ParallelProcessExecutor complete, proc_num={}.".format(self.proc_num))

    def execute(self, f, args):
        futures = [self.process_pool.submit(f, arg) for arg in args]
        ret_list = [future.result() for future in concurrent.futures.as_completed(futures)]
        return ret_list



class ParallelThreadExecutor(Executor):
    """多线程并发, IO密集适用"""

    def __init__(self, worker_num=None):
        self.worker_num = worker_num or cpu_count() * 5  # 手动设置, 或者按照CPU*5来设置
        self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=self.worker_num)
        logger.info("Initialize ParallelThreadExecutor complete, worker_num={}.".format(self.worker_num))

    def execute(self, f, args):
        futures = [self.thread_pool.submit(f, arg) for arg in args]
        ret_list = [future.result() for future in concurrent.futures.as_completed(futures)]
        return ret_list


if __name__ == "__main__":
    def func(x):
        time.sleep(20)
        return sum(x)

    args = [[1,2,3], [4,5,6]]
    executor = ParallelThreadExecutor()
    res = executor.execute(func, args)
    print(res)

    executor = ParallelProcessExecutor()
    res = executor.execute(func, args)
    print(res)
posted @ 2022-11-22 14:58  hyserendipity  阅读(420)  评论(0编辑  收藏  举报