github python100day_python day100-[day16-20]-6 并发编程

并发编程

20200309

参考

https://github.com/Damahuhu/Python-100-Days/blob/master/Day16-20/16-20.Python%E8%AF%AD%E8%A8%80%E8%BF%9B%E9%98%B6.md

Python中实现并发编程的三种方案:多线程、多进程和异步I/O。

并发编程的好处在于可以提升程序的执行效率以及改善用户体验;

坏处在于并发的程序不容易开发和调试,同时对其他程序来说它并不友好。

面试题:进程和线程的区别和联系?

进程 - 操作系统分配内存的基本单位 - 一个进程可以包含一个或多个线程

线程 - 操作系统分配CPU的基本单位

并发编程(concurrent programming)

提升执行性能 - 让程序中没有因果关系的部分可以并发的执行

改善用户体验 - 让耗时间的操作不会造成程序的假死

多线程

多线程:Python中提供了Thread类并辅以Lock、Condition、Event、Semaphore和Barrier。

Python中有GIL来防止多个线程同时执行本地字节码,这个锁对于CPython是必须的,因为CPython的内存管理并不是线程安全的,因为GIL的存在多线程并不能发挥CPU的多核特性。

多进程

多进程:多进程可以有效的解决GIL的问题,实现多进程主要的类是Process,其他辅助的类跟threading模块中的类似,进程间共享数据可以使用管道、套接字等,在multiprocessing模块中有一个Queue类,它基于管道和锁机制提供了多个进程共享的队列。下面是官方文档上关于多进程和进程池的一个示例。

说明:多线程和多进程的比较。

以下情况需要使用多线程:

程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。

程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。

以下情况需要使用多进程:

程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)。

程序的输入可以并行的分成块,并且可以将运算结果合并。

程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)。

异步处理

异步处理:从调度程序的任务队列中挑选任务,该调度程序以交叉的形式执行这些任务,我们并不能保证任务将以某种顺序去执行,因为执行顺序取决于队列中的一项任务是否愿意将CPU处理时间让位给另一项任务。异步任务通常通过多任务协作处理的方式来实现,由于执行时间和顺序的不确定,因此需要通过回调式编程或者future对象来获取任务执行的结果。Python 3通过asyncio模块和await和async关键字(在Python 3.7中正式被列为关键字)来支持异步处理。

"""

异步I/O - async / await

"""

import asyncio

def num_generator(m, n):

"""指定范围的数字生成器"""

yield from range(m, n + 1)

async def prime_filter(m, n):

"""素数过滤器"""

primes = []

for i in num_generator(m, n):

flag = True

for j in range(2, int(i ** 0.5 + 1)):

if i % j == 0:

flag = False

break

if flag:

print('Prime =>', i)

primes.append(i)

await asyncio.sleep(0.001)

return tuple(primes)

async def square_mapper(m, n):

"""平方映射器"""

squares = []

for i in num_generator(m, n):

print('Square =>', i * i)

squares.append(i * i)

await asyncio.sleep(0.001)

return squares

def main():

"""主函数"""

loop = asyncio.get_event_loop()

future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100))

future.add_done_callback(lambda x: print(x.result()))

loop.run_until_complete(future)

loop.close()

if __name__ == '__main__':

main()

# 说明

说明:上面的代码使用get_event_loop函数获得系统默认的事件循环,通过gather函数可以获得一个future对象,future对象的add_done_callback可以添加执行完成时的回调函数,loop对象的run_until_complete方法可以等待通过future对象获得协程执行结果。

说明:异步I/O与多进程的比较。

当程序不需要真正的并发性或并行性,而是更多的依赖于异步处理和回调时,asyncio就是一种很好的选择。

如果程序中有大量的等待与休眠时,也应该考虑asyncio,它很适合编写没有实时数据处理需求的Web应用服务器。

写在后面

Python还有很多用于处理并行任务的三方库,例如:joblib、PyMP等。实际开发中,要提升系统的可扩展性和并发性通常有垂直扩展(增加单个节点的处理能力)和水平扩展(将单个节点变成多个节点)两种做法。可以通过消息队列来实现应用程序的解耦合,消息队列相当于是多线程同步队列的扩展版本,不同机器上的应用程序相当于就是线程,而共享的分布式消息队列就是原来程序中的Queue。消息队列(面向消息的中间件)的最流行和最标准化的实现是AMQP(高级消息队列协议),AMQP源于金融行业,提供了排队、路由、可靠传输、安全等功能,最著名的实现包括:Apache的ActiveMQ、RabbitMQ等。

要实现任务的异步化,可以使用名为Celery的三方库。Celery是Python编写的分布式任务队列,它使用分布式消息进行工作,可以基于RabbitMQ或Redis来作为后端的消息代理。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值