c# 多线程 执行事件 并发_python语言特性之异步、并发与并行

在传统的系统编程中,并发操作采用事件驱动的多路复用模型来实现,而并行操作采用多线程或多进程模型,但在现在(Python/JS/C#)的编程中,在传统的并行操作层面上再实施一层封装,即采用任务(task)方式来管理并发和并行任务的处理,任务的方式则通过几个API实施复用,屏蔽底层实现的复杂性。

多路复用采用的是事件循环机制实现,程序中用于处理各种事件,通常程序都处于阻塞状态直到事件到达,事件到达后唤醒事件处理函数用于处理该事件,当事件处理完成后程序又处于阻塞状态,事件循环机制常用于事件、消息、信号的处理程序中,由于多个事件复用于同一线程中,这样的编程模型我们又称之为多路复用模型。

一个场景的多任务处理流程如下图,右侧各种任务处理函数则通过一个任务执行器实施统一管理。

bb7de074cf875b8fcd664b62846136fb.png

异步任务

并发任务则通过异步包asyncio库来实现任务的并行处理,该库通过yield from来转移程序的控制权即让自身函数进入阻塞状态,示例代码如下,每个包含yield from的函数均需配上装饰器asyncio.coroutine,该函数内不能有其他阻塞操作,可实现任意任务操作。

import asyncioimport itertoolsimport sys@asyncio.coroutinedef spin(msg):    write, flush = sys.stdout.write, sys.stdout.flush    for char in itertools.cycle('|/-\\'):        status = char + '  ' +msg        write(status)        flush()        write('\x08' * len(status))        try:            yield from asyncio.sleep(10)        except asyncio.CancelledError:            break        write('  ' * len(status) + '\x08' * len(status))@asyncio.coroutinedef slow_function():    # pretend waiting a long time for IO    yield from asyncio.sleep(3)    return 42    pass@asyncio.coroutinedef supervisor():    spinner = asyncio.async(spin("thinking!"))    print("spinner object:", spinner)    result = yield from slow_function()    spinner.cancel()    return resultdef main():    loop = asyncio.get_event_loop()    result = loop.run_until_complete(supervisor())    loop.close()    print('Answer:', result)if __name__ == "__main__":main()

如何将更多的并发任务提供到同一线程中来呢,多路复用则采用gather方法来实现:

        tasks = [item.run() for item in self.items]        loop = asyncio.get_event_loop()        loop.run_until_complete(asyncio.gather(*tasks))        loop.close()

多路复用的程序框架就是采用主线程事件循环方式,其余各类事件均可加入到循环中来,如果有事件到达就会唤醒对应的处理函数,这里就是yield from 阻塞后继续唤醒又从该函数点继续往下执行。

并发与并行任务

并发的方式有2种:线程级并发与进程级并发,在python中线程中因涉及到GIL全局锁的存在,线程级并行本质上也是并发任务,但python不同的是无论线程或者进程级并行两者在代码逻辑上处理一致,API函数也保持一致,具有很高的友好度。线程级并行示例代码如下:

from concurrent import futuresimport timeimport randomfrom datetime import datetimeimport threadingMAX_WORKERS = 10def download_one(n:int):    tid = threading.current_thread().ident    t = datetime.now()    print("time:%s, tid:%s, sleep n:%s" % (t, tid, n))    time.sleep(n)    return tid    passdef download_many():    workers = min(MAX_WORKERS, 100)    args = [random.randint(1,10) for i in range(workers)]    print("args:" % (args))    with futures.ThreadPoolExecutor(workers) as executor:        res = executor.map(download_one, args)    return len(res)def download_many2():    workers = min(MAX_WORKERS, 100)    args = [random.randint(1,10) for i in range(workers)]    print("args:" % (args))    with futures.ThreadPoolExecutor(workers) as executor:        to_do = []        for cc in args:            future = executor.submit(download_one, cc)            to_do.append(future)    rets = []    for future in futures.as_completed(to_do):        res = future.result()        msg = '{} result: {!r}'.format(future, res)        rets.append(res)        print(msg)    return len(rets)if __name__ == "__main__":download_many2()

进程级并行则只需要将ThreadPoolExecutor修改为ProcessPoolExectuor即可,具体修改如下:

futures.ProcessPoolExecutor(workers)

其余代码无需修改,这样就屏蔽了任务并行的差异性。Python中还有线程库(threading)和进程库(multiprocessing),而ThreadPoolExecutor和ProcessPoolExectuor都是在它们上面实施了一层封装,使之用起来更加简单方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值