多任务之进程

  • 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kun1280437633/article/details/80321342

1. 进程

程序:例如xxx.py这是程序,是一个静态的

进程:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。

不仅可以通过线程完成多任务,进程也是可以的

2. 进程的状态

工作中,任务数往往大于cpu的核数,即一定有一些任务正在执行,而另外一些任务在等待cpu进行执行,因此导致了有了不同的状态

  • 就绪态:运行的条件都已经慢去,正在等在cpu执行
  • 执行态:cpu正在执行其功能
  • 等待态:等待某些条件满足,例如一个程序sleep了,此时就处于等待态


3.进程的创建-multiprocessing

multiprocessing模块就是跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情

代码:

import multiprocessing  # 1.导入模块
import os  # 导入 os 模块

import time

def work1():
    print('work1的 pid:', os.getpid())   # getpid 获取当前进程的 pid
    print('work1的 ppid:', os.getppid())  # getppid 获取 父进程 pid
    for i in range(5):
        print('正在搬砖:', i)
        time.sleep(1)

def work2():
   print('work2的 pid:', os.getpid())
   print('work2的 ppid:', os.getppid())
   for i in range(5):
        print('正在扫地:', i)

        time.sleep(1)

def main():
    """使用进程完成多任务效果"""
    print('主进程的 pid:', os.getpid())
    print('主进程的 ppid:', os.getppid())
    t1 = multiprocessing.Process(target=work1)      # 2.创建对象
    t2 = multiprocessing.Process(target=work2)

    t1.start()  # 3.开启进程
    t2.start()

if __name__ == '__main__':

    main()

注意

        (1)进程的切换是随机的

        (2)主进程会等待子进程结束

        (3)主进程死亡会导致所有子进程死亡

4、让进程顺序执行

代码

import multiprocessing
import os 
# 1.导入 os 模块
import time

def work1():
    for i in range(5):
        print('正在搬砖:', i)
        time.sleep(1)

def work2():
    for i in range(5):
        print('正在扫地:', i)
        time.sleep(1)

def main():
    """使用进程完成多任务效果"""
    t1 = multiprocessing.Process(target=work1)
    t2 = multiprocessing.Process(target=work2)

    t1.start()

    # time.sleep(7)
    t1.join() 
# 冻结代码,直到 t1 运行结束

    t2.start()

if __name__ == '__main__':

    main()

5、进程间不能使用传参和全局变量共享数据

代码1

import multiprocessing

def work1(ls, *args, **kwargs):
    print(ls, args, kwargs)

def main():
 
  """主进程向子进程传参"""
    ls = [11,22]
    x = 100
    y = 200
    z = 300
    p1 = multiprocessing.Process(target=work1, args=(ls, x, y, z), kwargs={'name':'zs'})  # 主进程可以传递数据给子进程
    p1.start()

if __name__ == '__main__':

    main()

结果

[11, 22] (100, 200, 300) {'name': 'zs'}

代码2

import multiprocessing

def work1(ls):
    print('work1--', ls)
    ls.append(55)
    print('work1--', ls)

def work2(ls):
    print('work2--',ls)

def main():
 
  """主进程向子进程传参"""
    ls = [11,22]

    p1 = multiprocessing.Process(target=work1, args=(ls, ))
    p2 = multiprocessing.Process(target=work2, args=(ls, ))

    p1.start()
    p1.join()
    p2.start()

if __name__ == '__main__':

    main()

结果

work1-- [11, 22]
work1-- [11, 22, 55]

work2-- [11, 22]

代码3

import multiprocessing

def work1():
    print('work1--', ls)
    ls.append(55)
    print('work1--', ls)

def work2():
    print('work2--', ls)

ls = [11, 22] 
# 由于子进程的数据复制,无法使用全局变量在多个进程间共享数据
def main():
    """主进程向子进程传参"""

    p1 = multiprocessing.Process(target=work1)
    p2 = multiprocessing.Process(target=work2)

    p1.start()
    p1.join()
    p2.start()

if __name__ == '__main__':

    main()

结果

work1-- [11, 22]
work1-- [11, 22, 55]

work2-- [11, 22]

6、进程间通信使用Queue

代码

import multiprocessing

def work1(q):
    q.put(11) 
# p1进程放入数据
    print('work1 放入数据')

def work2(q):
    data = q.get()  # p2 进程可以取出数据
    print('work2 取出数据:', data)

def main():
    """创建两个进程,一个负责向队列放入数据,另一个负责从队列取出数据"""
    q = multiprocessing.Queue()

    p1 = multiprocessing.Process(target=work1, args=(q,))
    p2 = multiprocessing.Process(target=work2, args=(q,))

    p1.start()
    # p1.join()
    p2.start()

if __name__ == '__main__':

    main()

结果

work1 放入数据

work2 取出数据: 11

代码2;

import multiprocessing

def work1(q):
    for i in range(1000):
        q.put(i) 
# p1进程放入数据
        print('work1 放入数据')

def work2(q):
    while True:
        data = q.get()  # p2 进程可以取出数据
        print('work2 取出数据:', data)

        # if q.empty():  # 当队列没有数据则退出
        #     break

def main():
   
"""创建两个进程,一个负责向队列放入数据,另一个负责从队列取出数据"""
    q = multiprocessing.Queue()

    p1 = multiprocessing.Process(target=work1, args=(q,))
    p2 = multiprocessing.Process(target=work2, args=(q,))

    p1.start()
    p2.start()

if __name__ == '__main__':

    main()

7、进程池pool

当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。

初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务,请看下面的实例:

代码1

import multiprocessing
import os

def work1(i):
    print('第 %d 次运行,当前进程是:%d' % (i, os.getpid()))
    open('xxx.py').read()

def main():
 
  """使用进程池并发处理多个任务"""
    pool = multiprocessing.Pool(3)  # 1. 创建进程池

    for i in range(100):
        pool.apply_async(work1, (i,))  # 2. 添加任务,任务会立即运行

    # 主进程不会等待进程池运行结束
    pool.close()    # 关闭进程池,关闭后pool不再接收新的请求
    pool.join()    # 等待pool中所有子进程执行完成,必须放在close语句之后

if __name__ == '__main__':

    main()

multiprocessing.Pool常用函数解析:

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

如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到一条如下的错误信息:

RuntimeError: Queue objects should only be shared between processes through inheritance.

代码2

# 修改import中的Queue为Manager
from multiprocessing import Manager,Pool,Queue
import os,time,random

def reader(q):
    print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in range(q.qsize()):
        print("reader从Queue获取到消息:%s" % q.get(True))

def writer(q):
    print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
    for i in "gaohan":
        q.put(i)

if __name__=="__main__":
    print("(%s) start" % os.getpid())
    q = Manager().Queue() 
# 使用Manager中的Queue
    # q = Queue()
    po = Pool()

    po.apply_async(writer, (q,))

    time.sleep(1) 
# 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据

    po.apply_async(reader, (q,))
    po.close()
    po.join()

    print("(%s) End" % os.getpid())

结果

(31031) start
writer启动(31037),父进程为(31031)
reader启动(31037),父进程为(31031)
reader从Queue获取到消息:g
reader从Queue获取到消息:a
reader从Queue获取到消息:o
reader从Queue获取到消息:h
reader从Queue获取到消息:a
reader从Queue获取到消息:n

(31031) End

猜你喜欢

转载自blog.csdn.net/kun1280437633/article/details/80321342