Python并发编程1

1 并发编程

资源有限的情况下,怎么能最有效的利用资源。相当于统筹安排:做一顿午餐,炒菜,烧水,煮饭,在看有几个锅,几个人,怎么安排。在相同的时间内,干很多事情。
Python对这种并发编程是比较完善的。

我们会学习几个部分:

  • 第一:关于进程、线程、协程的基本概念
  • 第二:多线程、多进程、
  • 第三:协程
  • 第四:并发的实践

2 涉及到的基本概念

2.1 同步VS异步

同步和异步关注的是消息通信机制,可以理解成一种行为方式。它指的是多个任务之间的关系。

同步:调用方发出一个请求时,在没有得到被调用方的返回值之前,这个请求不会得到相应,处于等待状态,一旦调用方得到返回结果,请求得到了相应。
调用者主动等待被调用方返回结果,在没有返回值之前一直“专职”等待。

异步:和同步相反,异步是调用方发出请求之后,不会立刻得到反馈结果的时候,调用方可以去做其他的事情,当被调用者有反馈之后,通过状态、通知等形式来告诉调用者。
调用者发送请求之后,不会专职等待被调用方返回结果,而是当被调用方有结果饿之后通知调用方。

2.2 阻塞VS非阻塞

关注的是程序在等待调用结果(消息,返回值)时候的状态,描述的是一种状态。是对同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞。

阻塞:阻塞调用是指调用结果返回值之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回。
非阻塞:非阻塞调用指在没有得到结果之前,该调用不会阻塞当前线程。

概念区别:同步异步和阻塞与非阻塞

  • 阻塞与非阻塞跟同步异步没有必然的联系,是两个概念,两个角度。阻塞是使用同步机制的结果,非阻塞则是使用异步机制的结果。
  • 非阻塞不一定做到异步。非阻塞只是意味着方法调用后并不阻塞当前的任务,但是可以通过事件通知的方式给调用线程一个机会去完成。而异步不只是意味着方法调用不阻塞,它还意味着工作的完成已经转移到别的线程。

2.3 串行VS并行

串行:多个任务时,各个任务按照顺序执行,完成一个任务之后才能进行下一个任务。
并行:多个任务同时执行。

2.4 并行VS并发

区别从以下几点分析:

  • 时间上,并行指的是多个事件在同一时刻发生; 并发指的是多个事件在同一个时间间隔发生。其实是切换时间片轮流执行。
  • 作用点上,并行是作用在多个实体上的多个事件; 并发是作用在同一个实体上的多个事件。
  • 处理个数上,并行是多个处理器处理多个任务;并发是一个处理器处理多个任务。

2.5 IO密集型VS计算密集型

IO密集型和计算密集型指的是程序的类型。

IO密集型
这类程序是Cpu占用较低,因为Io的速度比CPU慢,大部分状态是cpu在等硬盘或者内存的IO(读写)操作。所以,这种程序对于处理的任务越多, cpu利用率越高。例如:读取文件,网络访问等等。

计算密集型
Cpu占用率高,而硬件或者内存io处理的相对少,这种主要是处理大量的科学计算。但这种如果任务越多,cpu任务就越重,所以,这时候,要高效的使用cpu,要适当使用多进程(后续课程会讲到)。
例如数据分析,程序建模关注迭代等任务。

2.6 并发的三个层次

低阶
针对于操作系统底层来实现并发,这一层的并发主要是直接使用原子操作的并发,是针对库的编写者而不是针对开发者的,一些基础库的包都是使用低阶并发。python不支持低阶并发。
中阶
这个并发几乎是所有语言都支持的 ,这个层次的并发不使用显式的原子操作,但是会使用显式的锁。Python提供了对这个层次从的编程,例如线程、进程、线程锁等等。
高阶
一些现代语言开始支持高级并发,这个层次没有显式的原子锁和原子操作,而是对中阶实现的过程进行了封装,出现的一些高阶并发框架。如协程、多线程、多进程的处理,python中有些已经开发好的第三方模块支持这部分的编程。

3 进程

3.1进程的概念

正在执行的一个过程或者一个任务,负责执行任务的是CPU,一个进程就是一个过程,一个任务

3.2.进程并发

CPU在同一个时刻,只能处理一个任务,只不过因为cpu处理的速度很快,在任务之间来回切换,所以让人感觉可以同时运行多个进程,也就是进程的并发,比如可以同时用pycharm和微信,还能上网。。

3.3进程的创建

进程的创建有fork()函数、multiprocessing.Process类两种方法。

3.3.1 fork()函数

一般在unix,linux或者mac系统创建。属于操作系统层面的一个应用,实际中一般不使用。

3.3.2 multiprocessing.Process类

multiprocessing.Process类的使用有两种方式:

  • 使用target参数,指定子进程
multiprocessing.Process(func ,args)pass
   Func是进程函数
Args是进程参数,必须是tuple类型。
  • 继承Process,重新run方法
   Class A (Process):
   def run(self):
      	pass

3.4 多进程创建和使用

除了进程池以外还有以下创建多进程的方式。

(1) multiprocessing

  • 直接使用Process创建子进程
    用start启动进程,run默认执行进程的方法。
    Join阻塞当前进程,将调用join的进行执行完之后再执行主进程。
  • 使用进程池
    我们要引用一下multiprocessing包下的Pool。
    Apply_async:异步申请模式

注意:Close不是直接关闭进程池,而是不接受其他新任务,暂时不接受新请求了。Terminate 是真正的关闭进程池所有的任务。Join阻塞主进程,等待子进程的退出。在close之后使用。使用apply同步申请方式,使用apply_async异步申请。

(2) 使用concurrent.futures.ProcessPoolExecutor
关于这一块的使用,在后面使用concurrent.futures模块完成多进程多线程的开发章节详细说明。

4线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它比进程小,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源,只具有一个线程的进程,可以叫做是单线程的。

4.1.全局解释所GIL

所有访问python对象的进程都将被一个全局锁GIL序列化。内存有很多程序,但是任意一个时刻只能有一个程序在运行,尽管python解释器中可以运行多线程,但这个多线程相当于“假的”,因为在任意一个时刻仍然是只有一个线程在执行。GIL的设计简化了Cpython的实现,锁住全局解释器比较容易对多线程的支持,但是也损失了多处理器主机的并行计算的能力。无法利用多核这个优势。

4.2.线程的创建和使用

Python提供了很多的模块来支持多线程编程,包括thread(2x),threading,Queue模块。
1.thread
注意:这是python2的创建线程的方式,不能在python3上使用。
2.threading

4 current.futures多线程多进程模块

该模块是python3.2引入的一个模块,之前的版本需要手动安装。
这个模块中有包括两个类:

  • ThreadPoolExecutor:线程池,提供异步调用
  • ProcessPoolExecutor: 进程池,提供异步调用

4.1 抽象类Executor

Executor是上面提到的两个类的父类,是一个抽象类,不能被直接调用,它为具体的异步执行定义了一系列的基本方法。这个Executor是一个上下文管理器,实现了__enter__和__exit__的代码,使得这个对象可以使用with操作符。当执行任务时,自动调用相关代码。

4.2 ThreadPoolExecutor和ProcessPoolExecutor

ThreadPoolExecutor和ProcessPoolExecutor都继承了Executor,是它的衍生类,使用方式很简单:

  • ThreadPoolExecutor(max_workers):定义线程池最大线程数
  • ProcessPoolExecutor(max_workers):定义进程池最大进程数。默认使用4个进程进行异步并发。

其中executor将作为每一个线程的调用者,负责将线程加入线程池准备就绪,等待cpu的执行。

4.3 相关方法

1.submit(fun, *args, **kwargs)
Executor中定义了submit(fun, *args, **kwargs)用于异步提交任务,往线程池加入task
其中fun是被异步调用的函数
*args, **kwargs是调用函数的参数,(不需要用元组形式调用。)
返回异步执行的任务future,可以理解成在未来完成的操作。使用future.result()获得异步调用该函数的返回值。如果有多个任务,可以循环,多次submit

2.Map()
取代for循环submit的操作
语法:map(func, *iterables, timeout=None, chunksize=1)
func是需要异步执行的函数
iterables可迭代对象
timeout是超时时间的设置,可以设置int或者float,超时后报异常,如果不设置则没有超时时间
3. as_completed(iterator,timeout)
获得已经执行完毕的任务,底层实现是生成器。
语法:
as_completed(iterator,timeout)
第一个参数是迭代器
Timeout默认为None,阻塞等待任务完成。返回迭代器对象。Timeout>0时,会等待时间阻塞,超过时间之后就不阻塞了。
注意这种调用,不是按照原来list的顺序

  1. wait(func,timeout,return_when)
    wait,可以返回一个元组,一个是执行完的,一个是还没有执行完的。
    语法:
    wait(func,timeout,return_when)
    return_when决定方法什么时间点返回:
    如果采用默认的ALL_COMPLETED,程序会阻塞直到线程池里面的所有任务都完成;
    如果用FIRST_COMPLETED参数,程序并不会等到线程池里面所有的任务都完成,但是至少第一个完成。

5.总结
1 map,代码简洁,而且执行起来,它的顺序是按照调用的顺序执行。但是它有缺点,它不能够传递不同的函数,只能是传递不同参数给同一个 函数。
2.submit和as_completed组合使用,不按调用顺序执行,但是它也有优点,就是它可以传递不同的参数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值