NO.4 Python多进程

零蚀


  • 多任务
  • 并发:cpu进行分配时间片,看起来像并行。
  • 并行:多个cpi同时工作,多个程序同时工作。
  • 进程编写
    启动
    调度
    满足条件
    结束
    等待条件
    新建
    就绪
    运行
    等待阻塞
    死亡
import multiprocessing as mp
import os


def function():
    print("加油")
    print(mp_process.name)


if __name__ == '__main__':
    mp_process = mp.Process(target=function)
    mp_process.start()
    print(mp_process.pid)  # 进程id
    print(mp.current_process())  # 当前进程对象
    print("cpu_count:", mp.cpu_count())  # cpu数量
    print("父进程id:", os.getppid())
  • 参数的传递
def function(a, b, c):
    print("加油", a, b, c)
    print(mp_process.name)


if __name__ == '__main__':
    # mp_process = mp.Process(target=function, args=(1, 2, 3))
    mp_process = mp.Process(target=function, kwargs={"a": 1, "b": 2, "c": 3})
    mp_process.start()
  • 进程的共享

进程是不能数据通信的,也就是说不可以做到数据共享,案例如下:

num = 0


def a():
    global num
    for i in range(100000):
        num = num + 1
    print("a:", num)


def b():
    global num
    for i in range(100000):
        num = num + 1

    print("b:", num)


if __name__ == '__main__':
    # mp_process = mp.Process(target=function, args=(1, 2, 3))
    mp_process = mp.Process(target=a)
    mp_process.start()
    mp_process1 = mp.Process(target=b)
    mp_process1.start()

    time.sleep(3)
    print("main:", num)

输出的结果为:

a: 100000
b: 100000
main: 0
  • 守护线程
# 终结当前进程
mp_process.terminate() 
# or 
# 设置守护进程()
mp_process.daemon=True

mp_process.daemon需要在start之前,且执行到时也是干掉子进程,并且干掉了子进程里面的所有缓存资源对象。

  • 消息队列

进程数据示意图:

进程1

进程2

主进程

被获取数据num

被获取数据num

数据new_num

数据new_num

数据num

添加消息队列

主进程

子进程

进程2

进程1

被获取数据num

被获取数据num

消息队列num num num num...num

数据num

数据new_num

数据new_num

  • 队列的特性
queue = multiprocessing.Queue(3)

queue.put(1)
queue.put("abcd")
queue.put(["aaa","bbb"])

# 如果队列已满,阻塞程序,直到队列熟取出
# queue.put(1234)

# 拿到队头,取出队列
head = queue.get()
print(head)
head1 = queue.get()
print(head1)
head2 = queue.get()
print(head2)

如果put 或get过多会导致阻塞,致使其他代码无法继续运行。为了防止阻塞导致崩溃。

# 添加不了,报异常
queue.put_nowait("adf")
# 获取不到,报异常
queue.get_nowait()

设置超时时间,这样也可以起到报错的效果

head4 = queue.get(timeout=3)
print(head4)

其他API

queue.full()  # 判满
queue.empty()  # 判空(3.7+ ;添加数据需要时间,在添加的过程中还是为空)
queue.qsize()  # 判size
  • 进程间通信
def put_data(queue):
for i in range(10):
    print("放数据:", i)
    queue.put(i)
    time.sleep(1)


def get_data(queue):
    while True:
        print("取数据:", queue.get(timeout=3))
        time.sleep(1)


if __name__ == '__main__':
    queue = mp.Queue()

    mp1 = mp.Process(target=put_data, args=(queue,))
    mp2 = mp.Process(target=get_data, args=(queue,))
    mp1.start()
    mp2.start()
  • 进程池
def funcA(str):
    time.sleep(1)
    print("任务A", str)


iif __name__ == '__main__':

    pool = mp.Pool(3)
    for i in range(10):
        # 指派任务,按照队列任务完成
        pool.apply(funcA, ("fjasfj;" + str(i),))

    pool.close()

阻塞当前代码,至当前进程池所有进程执行完毕。

pool.join()

在进程运行之前或之后运行相应代码。

result=pool.apply_async(funcA, ("f",))
# ....
result.wait()
  • 进程池的任务队列

进程池的队列需要特殊指定的消息队列:

def write(queue):
for i in range(10):
    print("写入数据",i)
    queue.put(i)
    time.sleep(1)


def read(queue):
    while True:
        print("读取数据",)
        value = queue.get()
        print("读取数据",value)


if __name__ == '__main__':
    queue = mp.Manager().Queue(3)
    pool = mp.Pool(2)

    pool.apply_async(write, (queue,))
    pool.apply_async(read, (queue,))
    pool.close()
    pool.join()

这里的线程池任务指定,需要指定为异步操作,不然会卡死在某一个apply上,由于队列大小不够。

其他API

import os

os.mkdir("path")
os.makedirs("paths")
# 遍历dir
os.listdir("path")
  • 进程锁

比如多个进程写同一个文件

import multiprocessing


# 对文件file进行写入操作
# "file.txt"

def write_one(file, lock):
    with lock:
        with open(file, "+a") as file1:
            for i in range(10):
                file1.write("aaa---"+str(i)+ "---")


def write_two(file, lock):
    with lock:
        with open(file, "+a") as file2:
            for i in range(10):
                file2.write("bbb---"+str(i)+"---")


if __name__ == '__main__':
    lock = multiprocessing.Lock()
    path = "file.txt"

    p1 = multiprocessing.Process(target=write_one, args=(path, lock))
    p2 = multiprocessing.Process(target=write_two, args=(path, lock))

    p1.start()
    p2.start()
  • fork()

fork主要存在于unix/linux下,也就是mac/linux系统系统,他调用就会产生一个子进程。windows没有fork无法运行,fork会返回两次,一个是当前进程,一个是子进程:

import os
import multiprocessing

fork_pid = os.fork()

print("pid:", fork_pid)
if fork_pid != 0:
    print('我是主进程', multiprocessing.current_process().name)
else:
        print('我是子进程', multiprocessing.current_process().name)

输出:

pid: 36396
我是主进程 34814
pid: 0
我是子进程 36395
  • PIPE管道

主要目的还是做进程间通信,PIPE分单双工和全双工,单双工是指通信双方只能有一个发起者和一个接收者,当存在发起者存在的时候,另一方只能由对方完毕之后,才能进行通信。

半双工

def sender_process(pipe):
for i in range(10):
    print("发送方——>pip2", i)
    pipe.send(i)
    time.sleep(1)


def receive_process(pipe):
    while True:
        print("接收方——>pip2", pipe.recv())
        time.sleep(1)


if __name__ == '__main__':
    # false 单双工, True 全双工
    pipe = mp.Pipe(duplex=False)

    p1 = mp.Process(target=receive_process, args=(pipe[0],))
    p2 = mp.Process(target=sender_process, args=(pipe[1],))

    p1.start()
    p2.start()

主要的方法是pipe.send(i)和pipe.recv()