美文网首页
39.1-多进程编程

39.1-多进程编程

作者: BeautifulSoulpy | 来源:发表于2020-02-02 08:24 被阅读0次

所谓成功,并不是看你有多聪明,而是看你能否笑着渡过难关;

这世界很公平,你想要最好,就一定会给你最痛!

Python之所以简单是因为:

  1. 它把所有的接口操作都变得一样,学习几乎没有什么负担;

总结

  1. 进程是独立的国家,都是自己管理自己的事情;线程间是国内的事情之间;线程可以共享进程里面的数据;但线程不可以共享线程之间的数据;
  2. CPU绑定;
  3. 但线程是没有线程安全问题的;都是线程安全的;
  4. 池就是容器,类似于甬道; #pool.apply_async(calc) # 异步非阻塞 # 同时在跑,不阻塞
    pool.apply(calc) # 同步阻塞:单进程依次执行;
  5. 进程、线程、IO 都是操作系统的功能;与编程语言无关,只不过实在里面做个一个封装而已;
  6. 进程、线程的概念、方法 都要使用,只是适用于不同的场景;解决不同的问题;

1. 多进程

多进程(multiprocessing)模块是在python 2.6版本中加入的。它最初由Jesse Noller和Richard Oudkerk在PEP 371 中定义。multiprocessing模块生成进程的方式就和你使用因为你使用的是多进程,所以你可以规避重叠解释锁(GIL),充分利用机器多处理器的优势。

由于Python的GIL,多线程未必是CPU密集型程序的好的选择。
多进程可以完全独立的进程环境中运行程序,可以充分地利用多处理器。
但是进程本身的隔离带来的数据不共享也是一个问题。而且线程比进程轻量级。

multiprocessing

Process类
Process类遵循了Thread类的API,减少了学习难度。
先看一个例子,前面介绍的单线程、多线程比较的例子的多进程版本;

# 多线程版本;
import threading
import logging
import datetime

logging.basicConfig(level=logging.INFO, format="%(thread)s %(message)s")
start = datetime.datetime.now()

# 计算
def calc():
    sum = 0
    for _ in range(1000000000):
        sum += 1

t1 = threading.Thread(target=calc)
t2 = threading.Thread(target=calc)
t3 = threading.Thread(target=calc)
t4 = threading.Thread(target=calc)
t5 = threading.Thread(target=calc)
t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t1.join()
t2.join()
t3.join()
t4.join()
t5.join()
delta = (datetime.datetime.now() - start).total_seconds()
logging.info(delta)
#------------------------------------------------
18056 194.114758
# 多进程版本;
import threading,multiprocessing
import logging
import datetime

logging.basicConfig(level=logging.INFO, format="%(thread)s %(message)s")
start = datetime.datetime.now()

# 计算
def calc():
    sum = 0
    for _ in range(1000000000):
        sum += 1

if __name__=="__main__":
# 多进程的启动要放到主模块代码中;
    ps = []

    for i in range(5):
        p = multiprocessing.Process(target=calc)
        ps.append(p)
        p.start()
    for p in ps:
        p.join()

    delta = (datetime.datetime.now() - start).total_seconds()
    print('end===')
    logging.info(delta)
#----------------------------------------------------------------------------------------------
30020 52.912196
end===

对于上面这个程序,在使用单线程、多线程都跑了4分钟多,而多进程用了1分半,这是真并行。
可以看出,几乎没有什么学习难度
注意: _name_ == "_main_" 多进程代码一定要放在这下面执行。

名称 说明
pid 进程id
exitcode 进程的退出状态码
terminate() 终止指定的进程

2. 进程间同步

Python在进程间同步提供了和线程同步一样的类,使用的方法一样,使用的效果也类似。
不过,进程间代价要高于线程间,而且底层实现是不同的,只不过Python屏蔽了这些不同之处,让用户简单使用多进程。

multiprocessing还提供共享内存、服务器进程来共享数据,还提供了用于进程间通讯的Queue队列、Pipe管道。(现在是网络编程,这些知识开发用不上)

通信方式不同

  1. 多进程就是启动多个解释器进程,进程间通信必须序列化、反序列化
  2. 数据的线程安全性问题
    如果每个进程中没有实现多线程,GIL可以说没什么用了

3. 进程池举例

multiprocessing.Pool 是进程池类;

名称 说明
apply(self, func, args=(),kwd={}) 阻塞执行,导致主进程执行其他子进程就像一个个执行
apply_async(self, func,args=(),kwds={},callback=None,error_callback=None) 与apply方法用法一致,非阻塞异步执行,得到结果后会执行回调
close() 关闭池,池不能再接受新的任务
terminate() 结束工作进程,不再处理未处理的任务
join() 主进程阻塞等待子进程的退出, join方法要在close或terminate之后使用

注意:调用谁的join方法,就是 join谁,就要等谁;

进程池的简单使用; 推荐使用 #pool.apply_async(calc) # 异步非阻塞 # 同时在跑,不阻塞

import threading,multiprocessing
import logging
import datetime

logging.basicConfig(level=logging.INFO, format="%(thread)s %(message)s")
start = datetime.datetime.now()

# 计算
def calc():
    sum = 0
    for _ in range(1000000000):
        sum += 1

if __name__=="__main__":
    ps = []
    # 进程池的使用;
    pool = multiprocessing.Pool(5)
    for i in range(5):
        #pool.apply_async(calc)  # 异步非阻塞  # 同时在跑,不阻塞
        pool.apply(calc)    #  同步阻塞:单进程一次执行
    pool.close()
    pool.join()

    delta = (datetime.datetime.now() - start).total_seconds()
    print('end===')
    logging.info(delta)
#---------------------------------------------------
end===
24984 49.851449

同步阻塞:单进程依次执行(效率低下,占着进程不用)

# !/usr/bin/env python
# encoding:utf-8
'''
@auther:administrator

'''

import threading,multiprocessing
import logging
import datetime

logging.basicConfig(level=logging.INFO, format="%(process)d %(processName)s %(thread)d %(message)s")
start = datetime.datetime.now()

# 计算
def calc():
    sum = 0
    for _ in range(100000000):
        sum += 1

if __name__=="__main__":
    ps = []
    # 进程池的使用;
    pool = multiprocessing.Pool(5)
    for i in range(5):
        # 回调函数必须接受一个参数
        pool.apply_async(calc,callback=lambda x: logging.info(x))  # 异步非阻塞  # 同时在跑,不阻塞
        #pool.apply(calc)    #  同步阻塞:单进程一次执行
    pool.close()
    pool.join()

    delta = (datetime.datetime.now() - start).total_seconds()
    print('end===')
    logging.info(delta)
#---------------------------------------------------------------------------
30836 MainProcess 32480 None
30836 MainProcess 32480 None
30836 MainProcess 32480 None
30836 MainProcess 32480 None
30836 MainProcess 32480 None
30836 MainProcess 23872 5.309782
end===
多进程、多线程的选择

1、CPU密集型
CPython中使用到了GIL,多线程的时候相互竞争,且多核又是不能发挥,Python多进程效率更高;

2、IO密集型
适合使用多线程,可以减少多进程间IO的序列化开销,且在IO等待的时候,切换到其他线程继续执行,效率不错。

应用

请求/应答模型:WEB应用中常见的处理模型
master启动多个worker工作进程(多线程模型比较合适),一般和CPU数目相同。发挥多核优势。
worker工作进程中,往往需要操作网络IO和磁盘IO,启动多线程,提高并发处理能力。worker处理用户的请求,往往需要等待数据,处理完请求还要通过网络IO返回响应。

这就是nginx工作模式。

相关文章

  • 39.1-多进程编程

    所谓成功,并不是看你有多聪明,而是看你能否笑着渡过难关; 这世界很公平,你想要最好,就一定会给你最痛! Pytho...

  • Python 并发编程简介

    1 多线程和多进程 Python语言中既有多线程编程也有多进程编程,也叫做并发编程。 多进程 把一个程序分成几个不...

  • 「进程」编程

    date: 2017-10-16 14:53:36title: 「进程」编程 图灵社区 - 理解UNIX进程: h...

  • 并发编程

    并发编程三种构造并发程序的方式:基于进程的并发编程、基于IO多路复用的并发编程、基于线程的并发编程1、 基于进程...

  • Gevent高并发网络库精解

    进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。 多进程编程在python...

  • Gevent

    前述 进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。 多进程编程在pyt...

  • Gevent简明教程

    前述 进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程、多线程、协程和异步。 多进程编程在pyt...

  • 【Java 基础你一定要掌握的知识点】多线程

    Java 给多线程编程提供了内置的支持。在多线程编程之前,我们需要先了解什么是线程。 进程和多线程简介 进程:进程...

  • 多进程介绍和多线程的比较

    多进程编程 耗CPU的操作,用多进程编程, 对于IO操作来说用多线程,进程切换的代价要高于线程 1. 对于耗CPU...

  • 多线程编程

    多线程编程 线程在Unix系统下,通常被称为轻量级的进程。一个进行可以有很都进程,每条线程并行执行不同的任务。 多...

网友评论

      本文标题:39.1-多进程编程

      本文链接:https://www.haomeiwen.com/subject/bdozactx.html