python 并发编程之进程

一、需要了解的概念

1、进程和程序的区别

程序就是一堆死的东西,没有生命周期
进程是有生命周期的,当一个任务进行完毕之后,进程就不存在了

2、cpu的工作机制

1.当CPU遇到I/O操作的时候,会剥夺CPU的执行权限

I/O密集型:
input output
遇到阻塞,但是不需要占用大量的CPU资源,需要等待,比如:sleep

2.当遇到的任务需要占用大量的计算时间的时候,也会剥夺执行权限

计算密集型
没有遇到阻塞,但是需要占用大量的CPU资源,也不需要等待

3、操作系统的调度算法

1. 先来先服务调度算法
2. 短作业优先调度算法
3. 时间片轮转法
4. 多级反馈队列

二、同步、异步、阻塞(BIO)、非阻塞(NIO)

阻塞(Blocking)和非阻塞(Non-blocking)关注的是任务的执行方式:

  • 阻塞:在阻塞模式下,任务会等待某个操作的完成,无法进行其他任务,直到操作完成才能继续执行。任务按照串行方式执行。
  • 非阻塞:在非阻塞模式下,任务不会等待操作的完成,而是立即返回并继续执行其他任务。任务可以通过轮询或回调等方式来检查操作是否完成。

同步(Synchronous)和异步(Asynchronous)关注的是任务的通信模式:

  • 同步:在同步模式下,任务发出一个请求后,会一直等待结果返回,期间无法执行其他任务。任务的执行顺序是有序的,依赖于前一个任务的结果。
  • 异步:在异步模式下,任务发出一个请求后,不需要等待结果返回,可以继续执行其他任务。任务的执行顺序可以是无序的,任务之间相互独立。

可以总结如下:

  • 阻塞和非阻塞描述任务的执行方式,阻塞模式下任务会等待操作完成,非阻塞模式下任务会继续执行。
  • 同步和异步描述任务的通信模式,同步模式下任务会等待结果返回,异步模式下任务不需要等待结果返回。
  • 阻塞和非阻塞主要关注任务自身的执行,同步和异步主要关注任务之间的通信方式。

需要注意的是,阻塞和同步以及非阻塞和异步并不完全等价。一个任务可以是阻塞同步的,即任务在等待操作完成时是阻塞的,而任务的通信方式是同步的。类似地,一个任务可以是非阻塞异步的,即任务在等待操作完成时是非阻塞的,而任务的通信方式是异步的。

同步阻塞式调用 ----> 效率最低

同步非阻塞式调用

异步阻塞式调用

异步非阻塞式调用 ----> 效率最高

进程与线程的一个简单解释 - 阮一峰的网络日志 (ruanyifeng.com)

三、开启进程

1、开启单进程

from multiprocessing  import Process
def task():
    print('this is a task')

# 拉起一个进程来完成task函数的操作
if __name__ == '__main__':
    p = Process(target=task) #  实例化进程对象
    p.start()   # 开启进程

注意:

  • if __name__ == '__main__': 在windos中要上,mac系统可以不加
  • multiprocessing 模块导入Process要大写。Process是类,process是方法。

2、Process类的参数

    def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
                 *, daemon=None):
        assert group is None, 'group argument must be None for now'
        count = next(_process_counter)
        self._identity = _current_process._identity + (count,)
        self._config = _current_process._config.copy()
        self._parent_pid = os.getpid()
        self._popen = None
        self._target = target
        self._args = tuple(args)
        self._kwargs = dict(kwargs)
        self._name = name or type(self).__name__ + '-' + \
                     ':'.join(str(i) for i in self._identity)
        if daemon is not None:
  • group(可选):分组参数,用于保留未来扩展的目的。通常情况下,该参数可以忽略,传入 None 即可。

  • target(可选):目标函数参数,表示要在新进程中执行的函数。当创建子进程时,该参数指定子进程要执行的代码逻辑。函数必须是可调用的对象。

  • name(可选):进程名称参数,用于设置进程的名称。可以在创建进程后通过 process.name 属性获取或设置进程的名称。

  •  *  p = multiprocessing.Process(target=worker, name='WorkerProcess')

    *  print(p.name) # 打印进程名

  • args(可选):位置参数元组,表示要传递给目标函数的参数。当创建子进程时,该参数传递给目标函数作为位置参数。参数会按照位置顺序传递给目标函数。

  • kwargs(可选):关键字参数字典,表示要传递给目标函数的关键字参数。当创建子进程时,该参数传递给目标函数作为关键字参数。参数会以关键字参数的方式传递给目标函数。

  • daemon(可选):守护进程参数,用于设置进程的守护状态。默认值为 None,表示继承父进程的守护状态。如果设置为 True,则子进程将被设置为守护进程,即当父进程结束时,子进程也会随之结束。如果设置为 False,则子进程将是非守护进程,即即使父进程结束,子进程也会继续执行。 

3、Process类的方法

from multiprocessing  import  Process
import time

def task():
    time.sleep(4)
    print('this is a task')

# 拉起一个进程来完成task函数的操作
# if __name__ == '__main__':
p = Process(target=task)  # 实例化进程对象

p.start()  # 开启进程
p.join()  # 先执行完子进程中的代码,再执行主进程中的代码
p.terminate()  # 杀死进程
print(p.is_alive())  # 查看进程是否存活

4、开启多进程

import time
from multiprocessing  import Process

def task(i):
    print("task:", i)
    time.sleep(1)

if __name__ == '__main__':
    start_time = time.time()

    ll = []  # 定义空列表存多进程
    for i  in range(5):
        # 开启多进程之后,有可能是没有顺序的
        p = Process(target=task, args=(i,))  # arg是传给目标函数的位置参数,已元组形式传,kwargs以字典形式传
        p.start()
        ll.append(p)

    for j  in ll:
        j.join()  #
    # 让所有的子进程先执行完,在执行主进程
    print(123)
    print("time:", time.time() - start_time)
# 输出结果:5个进程在1秒多执行完后执行主进程的代码
# task: 0
# task: 1
# task: 2
# task: 3
# task: 4
# 123
# time: 1.0228691101074219

、查看进程的id号

⚠️:1、p.pid 获取的子进程的id号。 2、os.getpid()获取的是当前进程的id号,os.getppid()获取的是当前进程的父进程id号

import os
import time
from multiprocessing import Process

def task():
    print('task中的子进程号:', os.getpid())  # 当前进程的id号
    print('主进程中的进程号:', os.getppid())  # 当前进程的父进程id号

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    time.sleep(10)
    print('子进程的进程号:', p.pid)
    print('主进程的进程号:', os.getpid())

五、进程间的通信机制(IPC)

在一个进程中不能修改另外一个进程的数据,言外之意是进程之间的数据是隔离的,互不影响

要想在一个进程中使用另外一个进程中的数据,需要让进程之间通信(IPC)

在Python中,有几种方式可以实现进程间通信(IPC,Inter-Process Communication),其中包括使用队列(Queue)、管道(Pipe)、共享内存(Shared Memory)等。

这里以队列为例实现进程间通信:

在主进程中拿到了子进程的a变量,实现进程间的通信

from multiprocessing import  Process,Queue
def task(q):
    a = 1
    q.put('这是子进程写入的数据!')
    q.put(a)

if __name__ == '__main__':
    q = Queue(4)
    p = Process(target=task, args=(q, ))
    p.start()

    # 在主进程中取出子进程写的的数据
    print(q.get())
    print(q.get())

  

 

posted @ 2023-07-05 15:12  凡人半睁眼  阅读(48)  评论(0编辑  收藏  举报