python进阶--多进程

进程

  • 进程

具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元.

程序是指令、数据及其组织形式的描述,进程是程序的实体.

在当代面向线程设计的计算机结构中,进程是线程的容器.

程是系统进行资源分配和调度的独立单元,线程是CPU调度和分配的基本单位。(一个qq程序可以理解为1个进程,1个qq聊天窗口可以理解为1个线程)

multiprocessing

  • multiprocessing模块-基于进程的并行

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了multiprocessing。

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

  • 参考文档:

官方文档:https://docs.python.org/3/library/multiprocessing.html

进程创建–Process类

# 构造方法
Process([group [, target [, name [, args [, kwargs]]]]])
  group: 线程组,目前还没有实现,库引用中提示必须是None; 
  target: 要执行的方法; 
  name: 进程名; 
  args/kwargs: 要传入方法的参数。

# 实例方法
  is_alive():返回进程是否在运行。
  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
  start():进程准备就绪,等待CPU调度
  run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
  terminate():不管任务是否完成,立即停止工作进程

# 属性
  authkey
  daemon:和线程的setDeamon功能一样
  exitcode(进程在运行时为None、如果为–N,表示被信号N结束)
  name:进程名字
  pid:进程号

方式一:直接传入要运行的方法创建多进程

from multiprocessing import Process
import threading
import time


def foo(i):
    print('say hi', i)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=foo, args=(i,))
        p.start()

>
say hi 2
say hi 0
say hi 1
say hi 4
say hi 3
say hi 5
say hi 6
say hi 7
say hi 8
say hi 9

方式二:继承Process类并重写run()方法

from multiprocessing import Process
import time
class MyProcess(Process):
    def __init__(self, arg):
        super(MyProcess, self).__init__()
        self.arg = arg

    def run(self):
        print('say hi', self.arg)
        time.sleep(1)
    
if __name__ == '__main__':

    for i in range(10):
        p = MyProcess(i)
        p.start()

进程id及进程关系查看

from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:', __name__)
    # 得到父亲进程的id
    print('parent process:', os.getppid())
    # 得到本身进程的id
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)

if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
>
main line
module name: __main__
parent process: 54134
process id: 54137
function f
module name: __mp_main__
parent process: 54137     
process id: 54139
hello bob

进程间通信–Queue

进程间通信

  • 必要性: 进程间空间独立,资源不共享(无法共享全局变量),此时在需要进程间数据传输时就需要特定的手段进行数据通信。
  • 常用进程间通信方法:管道通信、消息队列、共享内存、信号量
  • 使用multiprocessing.Queue可以在进程间通信,但不能在Pool池创建的进程间进行通信

multiprocessing.Queue()和queue.Queue区别

  • queue.Queue是进程内(线程)非阻塞队列,multiprocessing.Queue()是跨进程通信队列
  • queue.Queue各进程私有,multiprocessing.Queue各子进程共有。
# 进程通信--multiprocessing.Queue()
from multiprocessing import Process, Queue


def work1(q):
    while True:
        if not q.empty():
            n = q.get()
            print('work1正在执行{}的平方,结果是{}'.format(n,str(int(n)*int(n))))
        else:
            break


def work2(q):
    while True:
        if not q.empty():
            n = q.get()
            print('work2正在执行{}的平方,结果是{}'.format(n,str(int(n)*int(n))))
        else:
            break


if __name__ == "__main__":
    q = Queue()
    for i in range(10):
        q.put(i)

    p1 = Process(target=work1, args=(q,))   # 进程间的Queue()需要当作参数传入
    p2 = Process(target=work2, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

>
work2正在执行0的平方,结果是0
work2正在执行1的平方,结果是1
work2正在执行2的平方,结果是4
work1正在执行3的平方,结果是9
work2正在执行4的平方,结果是16
work1正在执行5的平方,结果是25
work2正在执行6的平方,结果是36
work1正在执行7的平方,结果是49
work2正在执行8的平方,结果是64
work2正在执行9的平方,结果是81
# 生产者消费者模型
from multiprocessing import Process, Queue, Pool
import time


def producer(queue):
  queue.put('A')
  time.sleep(2)

def consumer(queue):
  time.sleep(2)
  data = queue.get()
  print(data)

if __name__ == '__main__':
  queue= Queue()
  p = Process(target=producer, args=(queue,))
  c = Process(target=consumer, args=(queue,))
  p.start()
  c.start()
  p.join()
  c.join()

进程池–Pool

  • 进程池可以控制进程的数量,重复利用进程对象,减少创建和销毁进程的开销。
  • 创建进程池可以接收一个参数,这个参数可以设置进程池的最大值。就是指定有几个子进程在“同时”进行。(为什么说是“同时”,因为在并发的情况下,子进程和主进程是按照时间片轮寻的方式执行的,只是切换得过快,并不是真的一起运行。)

Pool常用方法

apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
apply(func[, args[, kwds]]):使用阻塞方式调用func
close():关闭Pool,使其不再接受新的任务;
terminate():不管任务是否完成,立即终止;
join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
import multiprocessing
import os
import random
import time

def run_time(index):
    start_time = time.time()
    time.sleep(random.random())
    print("任务%d  任务id为%d  任务运行的时间为%0.2f" % (index,os.getpid(),
                                            time.time()-start_time))


if __name__ == '__main__':
    pool = multiprocessing.Pool(3)   #创建进程池,并设置进程数量为3 
    for i in range(10):
        pool.apply_async(func=run_time,args=(i,))  # 启动10个进程
    pool.close()   # 必须先关闭进程池,不再让它接收新的进程,才能进行下一步的阻塞。(已经在排队的进程不算新进程了!例如上面的例子,10个子进程已经在进程池排队,所以join方法会阻塞直到10个子进程执行完成。)
    pool.join()

>
任务2  任务id60426  任务运行的时间为0.22
任务0  任务id60424  任务运行的时间为0.38
任务1  任务id60425  任务运行的时间为0.96
任务3  任务id60426  任务运行的时间为0.89
任务4  任务id60424  任务运行的时间为0.94
任务5  任务id60425  任务运行的时间为0.42
任务6  任务id60426  任务运行的时间为0.30
任务9  任务id60426  任务运行的时间为0.44
任务8  任务id60425  任务运行的时间为0.89
任务7  任务id60424  任务运行的时间为0.98

> 从结果可以看出,只启动了3个进程去执行任务,其他进程都在排队

进程池通信–Manager.Queue

使用multiprocessing.Manager.Queue可以在Pool进程池创建的进程间进行通信

  • 参考文档:https://www.jb51.net/article/182551.htm
from multiprocessing import Process, Queue, Pool, Manager
import time


def producer(queue):
  queue.put('A')
  time.sleep(2)


def consumer(queue):
  time.sleep(2)
  data = queue.get()
  print("consumer:%s" % data)


if __name__ == '__main__':
#   queue = Queue(10) # 这个是使用multiprocessing.Queue,无效
  queue = Manager().Queue(10) # 这个是使用multiprocessing.Manager.Queue, 可以
  pool = Pool(2)
  pool.apply_async(producer, args=(queue,))
  pool.apply_async(consumer, args=(queue,))
  pool.close()
  pool.join()
>
consumer:A

猜你喜欢

转载自blog.csdn.net/qq_25672165/article/details/111572486
今日推荐