阿鲲的博客 主修软件工程和算法模型,极客成长中

python多进程/多进程

2019-03-16
jktian

阅读:


进程&线程

进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位.进程之间的内存空间是隔离的 多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间 进程之间是竞争关系,线程之间是协作关系

利用multiprocessing库,使用多进程,而不是多线程,充分利用多核cpu资源

多线程指的是,在一个进程中开启多个线程,简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。详细的讲分为4点:

  1. 多线程共享一个进程的地址空间
  2. 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
  3. 若多个线程都是cpu密集型的,那么并不能获得性能上的增强,但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。
  4. 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于python)

多线程用于IO密集型,如socket,爬虫,web 多进程用于计算密集型,如金融分析

Process

  • 两种创建方式:
    1. 创建进程,传入参数为要执行的函数以及函数参数。一个进程就是一个Proccess类的实例
    2. 自定义类,继承Process类,重载run函数
  • 进程的deamon属性。若为true, 表示父进程结束,则子进程自动终止
  • 子进程的join函数。表示父进程等待子进程执行完毕

Lock

  • 避免并行导致输出错位。让同一时间只有一个进程操作临时资源
  • Lock类的实例。有acquire()和release()方法
  • Pcocess之间不共享数据,但是共享同一套文件系统,所以访问同一文件或同一个终端,会存在访问冲突。即,可以用文件来让进程间通信/共享数据,但存在效率问题和需要自己加锁处理

GIL

GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

可以肯定的一点是:保护不同的数据的安全,就应该加不同的锁。

要想了解GIL,首先确定一点:每次执行python程序,都会产生一个独立的进程。例如python test.py,python aaa.py,python bbb.py会产生3个不同的python进程

GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理

有了GIL的存在,同一时刻同一进程中只有一个线程被执行

Semaphore 信号量

  • 控制临界资源的数量,保证进程之间的互斥和同步
  • semaphore和mutex结合使用。mutex是互斥锁

IPC

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的

  • 生产者消费者模型利用了阻塞队列
  • 队列就是管道加锁实现的

    Queue 共享队列

  • 用于进程间通信,不同与普通的队列。
  • 有空异常和满异常

    Pipe

  • 一端的进程发,一端的进程收
  • 单向或双向
  • shell解释器中,一个命令就是一个进程,可以通过管道通信
  1. 加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 虽然可以用文件共享数据实现进程间通信,但问题是:
    • 效率低(共享数据基于文件,而文件是硬盘上的数据)
    • 需要自己加锁处理
  2. 因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
    • 队列和管道都是将数据存放于内存中
    • 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来, 我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

Pool 进程池

  • 不再手动创建进程。用pool指定数量,如果没满,创建新的;如果满的,等待
  • 阻塞式和非阻塞式
  • map函数
  • 进程池可以得到进程执行的结果,而普通的进程是将结果存在队列中。

同步/异步/阻塞/非阻塞

同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成, 而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当, 函数返回的时候通过状态、通知、事件等方式通知进程任务完成。

阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程

僵尸进程&孤儿进程

僵尸进程和孤儿进程:僵尸进程是没有被父进程释放资源的进程,需要父进程调用wail()或者join()(join方法中使用了wait方法);孤儿进程是父进程已经被kill的进程,会被init进程接受然后释放资源

生产者消费者模型

生产者消费者模式是通过一个容器来解决生产者的强耦合问题。生产者和消费者彼此之间不直接通讯而通过阻塞队列来进行通讯,所以生产者生产 完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡 了生产者和消费者的处理能力。

通过消息队列交换数据。这样极大地减少了对使用锁定和其他同步手段的需求

Event 事件

Event对象(包含一个可由线程设置的信号标志),用于线程间的同步,线程等待某个事件,否则一直处于阻塞状态


上一篇 cpp/c在oj

下一篇 python黑魔法

Comments

Content