13、Python多进程

目录

使用subprocess创建进程

打开其他的exe也是创建进程的一种形式,不是吗
比如要打开cmd,然后输入命令ping www.baidu.com

# !/usr/bin/env python
# encoding: utf-8

import subprocess

ret = subprocess.Popen(['ping.exe', 'www.baidu.com'],
                       stdin=subprocess.PIPE,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       shell=True)
out = ret.stdout.read()
gbk_data = out.decode('gbk')
print(gbk_data)

或者是使用ipconfig查看本机ip地址

# !/usr/bin/env python
# encoding: utf-8

import subprocess

ret = subprocess.Popen(['ipconfig.exe'],
                       stdin=subprocess.PIPE,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       shell=True)
out = ret.stdout.read()
gbk_data = out.decode('gbk')
print(gbk_data)

更多使用方式请参考:
https://www.cnblogs.com/sunailong/p/5162748.html


使用multiprocessing

Multiprocessing中的Process描述一个进程的对象,接受两个参数,一个是传入一个目标函数target,和参数的元组args;
p.start()启动对应的线程,但只要当主进程使用.join后(或者使用sleep)子线程才有机会去运行

使用multiprocessing创建单个进程

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os

def do_soming(what):
    print("process id:{0}, do what:{1}".format(os.getpid(), what))#获取当前进程的id

if __name__ == '__main__':
    p = mp.Process(target=do_soming, args=('zengraoli',))#创建进程
    p.start()#启动进程
    p.join()#等待进程结束

使用multiprocessing创建多个进程

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def do_soming(*what):
    print("process id:{0}, number:{1}, do what:{2}".format(os.getpid(), what[0], what[1]))#获取当前进程的id
    time.sleep(5)#等待5秒后退出

if __name__ == '__main__':
    for i in range(4):
        p = mp.Process(target=do_soming, args=(str(i), 'zengraoli',))# 创建进程
        print("item_process.start")
        p.start()#启动进程
    p.join()#阻塞等待进程

    print("main close")

使用terminate()终止进程

当发现某个进程等待时间过长了或者控制不住了,可以使用terminate()来终止

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def do_soming(*what):
    while True:
        print("控制不住自己了..process id:{0}, number:{1}, do what:{2}".format(os.getpid(), what[0], what[1]))  # 获取当前进程的id

if __name__ == '__main__':
    process_list = []#存放所以进程对象的list
    for i in range(4):
        p = mp.Process(target=do_soming, args=(str(i), 'zengraoli',))#创建进程
        print("item_process.start")
        process_list.append(p)
        p.start()#启动进程

    time.sleep(10)
    #等待所有进程
    for item_process in process_list:
        item_process.terminate()#杀掉那些二货

    print("main close")


进程池Pool

我们其实能创建的进程数是有限的,毕竟cpu的能力也就这么多了,加上创建进程是一个比较耗时的操作,如果利用了Pool,预先分配一些进程,当有任务过来的时候,假如有空闲线程,那么任务立即执行,否则进行等待,这大大提高了效率

比如下面的程序,预先分配了4个线程,但我需要执行的任务是20个,那么可以用Pool这么做

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def do_soming(*what):
    print("控制不住自己了..process id:{0}, number:{1}, do what:{2}".format(os.getpid(), what[0], what[1]))  # 获取当前进程的id
    time.sleep(5)
    print("process id:{0} end".format(os.getpid()))

if __name__ == '__main__':
    p = mp.Pool(processes=4)
    for i in range(10):
        p.apply_async(do_soming, args=(str(i), "zeng"))
    print("waiting...")
    p.close()
    p.join()
    print("main close")

你可以看到每次是4个进程启动、结束的
apply_async是每次都会同时调用所以的进程池的进程一起执行
这里写图片描述

Pool中一次性开启的N个进程,貌似在内部按顺序执行的,不知道会不会是个坑

apply会保证池子中只同时运行一个进程,是不是看起来一般情况下用这个函数的不多呢

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def do_soming(*what):
    print("控制不住自己了..process id:{0}, number:{1}, do what:{2}".format(os.getpid(), what[0], what[1]))  # 获取当前进程的id
    time.sleep(5)
    print("process id:{0} end".format(os.getpid()))

if __name__ == '__main__':
    p = mp.Pool(processes=4)
    for i in range(10):
        p.apply(do_soming, args=(str(i), "zeng"))
    print("waiting...")
    p.close()
    p.join()
    print("main close")

这里写图片描述

假如进程只需要做某种运算,Pool给我们提供了map操作,只需要传入对应的数据即可,例如要对数据进行求平方的操作

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp

def do_soming(x):
    print("do_soming begin.")
    return list(map(lambda i:i**2, x))

if __name__ == '__main__':
    x = [list(range(5)), list(range(5, 10)),
        list(range(10, 15)), list(range(15, 20))]

    with mp.Pool(4) as p:
        print(p.map(do_soming, x))

    print("main end.")

这里写图片描述

apply_async还能接受返回值,但是获取返回值是阻塞的

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import time


def do_soming(x):
    print("do_soming begin.")
    time.sleep(5)
    return x**2


if __name__ == '__main__':
    res_list = []
    with mp.Pool(4) as p:
        for i in range(10):
            res = p.apply_async(do_soming, (i, ))
            res_list.append(res)
        # 异步apply_async用法:如果使用异步提交的任务,主进程需要使用join,
        # 等待进程池内任务都处理完,然后可以用get收集结果,否则,主进程结束,
        # 进程池可能还没来得及执行,也就跟着一起结束了
        p.close()
        p.join()
        print("begin waitting.")
        for res in res_list:
            print(res.get())#阻塞,等等到所有的线程都返回了才会执行下一行
        print("end waitting.")

    print("main end.")

进一步学习请参考
https://www.cnblogs.com/freeman818/p/7154089.html
https://www.cnblogs.com/alan-babyblog/p/5351031.html


进程间的通讯

进程间的通讯有点类似生产者和消费者(一般这个举例会出现在操作系统里边),一个进程A给另外一个进程B写入数据,当B发现有数据时就输出,没有时就等待A写入;在这个过程中就用到了某个容易来临时存放数据,所以有了以下的几个小知识

Queue对象

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import time

def put_data(q, data):
    print("in put_data.")
    for i in data:
        q.put(i)#取出元素后从Queue中删除
        time.sleep(1)

def get_data(q):
    print("in get_data.")
    while True:
        print(q.get())#如果没有数据,则会等待,可以设置timeout

if __name__ == '__main__':
    q = mp.Queue()
    p_list = []
    data_list = [['zeng1', 'zeng2', 'zeng3', 'zeng4'],
                 ['zeng5', 'zeng6', 'zeng7', 'zeng8'],
                 ['zeng9', 'zeng10', 'zeng11', 'zeng12']]
    put_process1 = mp.Process(target=put_data, args=(q, data_list[0]))
    put_process2 = mp.Process(target=put_data, args=(q, data_list[1]))
    put_process3 = mp.Process(target=put_data, args=(q, data_list[2]))
    get_process = mp.Process(target=get_data, args=(q, ))

    put_process1.start()
    put_process2.start()
    put_process3.start()
    get_process.start()

    put_process1.join()
    put_process2.join()
    put_process3.join()

    time.sleep(10)
    print("main end.")
get_process.terminate()

程序启动了3个put进程,对Queue写入数据,启动一个get进程从里面取出数据(死循环,自己不会退出),并没有对get进程进行join,因此主线程不会等待get进程执行完毕后再退出,因此只需要put进程结束后,就给get进程发出terminate结束信息

但是如果想使用Pool来简化进程的操作会失败,需要改成在Pool中对Queue进行初始化
参考(相当于把q保存到了全局中,不建议这么用,还是使用manager来管理):

https://blog.csdn.net/tpoy0099/article/details/50324485

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import time

def put_data(data):
    print("in put_data.")
    for i in data:
        get_data.q.put(i)#取出元素后从Queue中删除
        time.sleep(1)

def get_data(q):
    print("in get_data.")
    while True:
        print(q.get())#如果没有数据,则会等待,可以设置timeout

def queue_init(q):
    get_data.q = q

if __name__ == '__main__':
    q = mp.Queue()
    p_list = []
    data_list = [['zeng1', 'zeng2', 'zeng3', 'zeng4'],
                 ['zeng5', 'zeng6', 'zeng7', 'zeng8'],
                 ['zeng9', 'zeng10', 'zeng11', 'zeng12']]

    get_process = mp.Process(target=get_data, args=(q,))
    p = mp.Pool(3, queue_init, (q, ))
    for i in range(3):
        p.apply_async(put_data, (data_list[i], ))
    p.close()
    get_process.start()
    p.join()
    time.sleep(10)
    print("main end.")
    get_process.terminate()

Pipe对象

Pipe也是实现进程间数据传递的一种形式,其实是使用的共享内存实现的,跟Queue差不多原理

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp

def child_pipe_set(child_pipe):
    child_pipe.send('hello world.')#往管道中灌入数据
    print(child_pipe.recv())#等待回复,会阻塞
    child_pipe.close()#关闭当前管理的一端

def parent_pipe_set(parent_pipe):
    print(parent_pipe.recv()) # 获取管道中的数据,会阻塞
    parent_pipe.send("ok.")
    parent_pipe.close() # 关闭当前管理的一端

if __name__ == '__main__':
    parent_pipe, child_pipe = mp.Pipe()
    child_process = mp.Process(target=child_pipe_set, args=(child_pipe, ))
    parent_process = mp.Process(target=parent_pipe_set, args=(parent_pipe, ))
    child_process.start()
    parent_process.start()
    child_process.join()
    parent_process.join()
    print("main end.")

Manager对象

Manager对象其实是也是一个进程,拥有list、dict、Queue等的服务器端进程,既可以在本机进行通讯,也可以在不同的电脑上进行,下面是一个使用Manager对象中Queue进行进程间数据交换的例子

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import time

def put_data(q, data):
    print("in put_data.")
    for i in data:
        q.put(i)#取出元素后从Queue中删除
        time.sleep(1)

def get_data(q):
    print("in get_data.")
    while True:
        print(q.get())#如果没有数据,则会等待,可以设置timeout

if __name__ == '__main__':
    q = mp.Manager().Queue()
    data_list = [['zeng1', 'zeng2', 'zeng3', 'zeng4'],
                 ['zeng5', 'zeng6', 'zeng7', 'zeng8'],
                 ['zeng9', 'zeng10', 'zeng11', 'zeng12']]
    get_process = mp.Process(target=get_data, args=(q, ))
    get_process.start()
    with mp.Pool(3) as p:
        print(data_list.__len__())
        for i in range(data_list.__len__()):
            print(i)
            p.apply_async(put_data, (q, data_list[i], ))
        p.close()
        p.join()

    time.sleep(10)
    print("main end.")
    get_process.terminate()

多进程的同步

多进程的同步有某些地方会用得着,比如某个资源、某段代码同一时间有且仅能有一个进程进行使用,比如有N个进程同时需要修改队列的数据

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def show_info(q, num):
    print("show_info, process id:{0}, num value:{1}".format(os.getpid(), num))
    total = q.get()
    time.sleep(2)
    total += num
    q.put(total)

if __name__ == '__main__':
    #创建4个进程
    q = mp.Queue()
    total = 0
    q.put(total)
    start_time = time.time()
    for i in range(4):
        p = mp.Process(target=show_info, args=(q, i, ))
        p.start()#让线程跑起来
    p.join()#主线程等待

    end_time = time.time()
    print(q.get())
    print(end_time - start_time)
print("main end.")

嗯,据说是不安全的,因为有可能在A进程修改的时候,B进程也在修改…不过可能用的是mp里面的Queue,可能不会出现这种情况(但,就假装会出现吧,不然下面没法继续了…)

Lock对象

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def show_info(lock, q, num):
    lock.acquire()#获取锁
    try:
        print("show_info, process id:{0}, num value:{1}".format(os.getpid(), num))
        total = q.get()
        time.sleep(2)
        total += num
        q.put(total)
    except Exception:
        print(Exception)
    finally:
        pass
        lock.release()#解开锁

if __name__ == '__main__':
    #创建4个进程
    q = mp.Queue()
    total = 0
    q.put(total)
    lock = mp.Lock()#定义一个锁
    start_time = time.time()
    for i in range(4):
        p = mp.Process(target=show_info, args=(lock, q, i, ))
        p.start()#让线程跑起来
    p.join()#主线程等待

    end_time = time.time()
    print(q.get())
    print(end_time - start_time)
    print("main end.")

可以看到还是有点效果的,毕竟速度慢得可以

Event对象

# !/usr/bin/env python
# encoding: utf-8

import multiprocessing as mp
import os
import time

def show_info(event, q, num):
    if event.is_set():#等待时间状态为可设置
        event.wait()#等待线程
        try:
            print("show_info, process id:{0}, num value:{1}".format(os.getpid(), num))
            total = q.get()
            time.sleep(2)
            total += num
            q.put(total)
        except Exception:
            print(Exception)
        finally:
            event.clear()
    else:
        event.set()#设置事件状态为可设置

if __name__ == '__main__':
    #创建4个进程
    q = mp.Queue()
    total = 0
    q.put(total)
    event = mp.Event()#定义一个锁
    start_time = time.time()
    for i in range(4):
        p = mp.Process(target=show_info, args=(event, q, i, ))
        p.start()#让线程跑起来
    p.join()#主线程等待

    end_time = time.time()
    print(q.get())
    print(end_time - start_time)
print("main end.")

当然还可以利用Queue或者Pipe来做同步

https://blog.csdn.net/mydear_11000/article/details/52778619

更多进程的知识,请参阅

https://docs.python.org/3.6/library/multiprocessing.html


猜你喜欢

转载自blog.csdn.net/zengraoli/article/details/81320523