同步异步、mutiprocessing创建进程process模块及进程对象的多种方法、消息队列Queue

同步异步

用来表达任务的提交方式

同步:提交任务之后原地等待任务的返回结果 期间不做任何事

异步:提交任务之后不愿等待任务的返回结果 直接去做其他事 有结果自动通知

eg:

​ 同步:客户端发送请求给服务端,在等待服务端响应的请求时,客户端不做其他的事情。当服务端做完了才返回到客户端。这样的话客户端需要一直等待

​ 异步:当客户端发送给服务端请求时,在等待服务端响应的时候,客户端可以做其他的事情,这样节约了时间,提高了效率

阻塞与非阻塞

用来表达任务的执行状态

阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关 也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的角度来说的

阻塞:阻塞态

非阻塞:就绪态、运行态

综合使用

同步阻塞:效率最低 你一直在原地专心等待啥事也不干

异步阻塞:异步操作是可以被阻塞的 只不过他不是在处理消息时阻塞 而是在等待消息通知时被阻塞

同步非阻塞:需要在这两种不同的行为之间来回切换 效率低下

异步非阻塞:效率最高

总结:阻塞和非阻塞描述的是程序在等待调用结果(消息 返回值)时的状态;同步和异步描述的是消息通信的机制

创建进程的多种方式之multiprocess.process模块

'''
1.鼠标双击软件图标
2.python代码创建进程
'''
multiprocess.process模块
process模块是一个创建进程的模块

from multiprocessing import Process
import time

def task():
    print('task is running')
    time.sleep(3)
    print('task is over')

p1 = Process(target=task)
p1.start()
print('主进程')
'''
在不同操作系统中穿创建进程底层原理不一样
windows
	以导入模块的形式创建进程
linux/mac
	以拷贝代码的形式创建进程
'''

image

方式一

from multiprocessing import Process
import time

def task(name):
    print('task is running', name)
    time.sleep(3)
    print('task is over', name)


if __name__ == '__main__':
    # p1 = Process(target=task, args=('jason',))  # 位置参数
    p1 = Process(target=task, kwargs={'name': 'jason'})  # 关键字参数

    p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
    print('主进程')

image

方式二

from multiprocessing import Process
import time


class MyProcess(Process):
    def run(self):
        print('run is running')
        time.sleep(3)
        print('run is over')

if __name__ == '__main__':
    obj = MyProcess()
    obj.start()
    print('我是主进程的')
    
# 传参
from multiprocessing import Process
import time


class MyProcess(Process):
    def __init__(self, name, age):  # 传值需要从新重写双下init方法
        super().__init__()  # 这里init括号不传参是因为 父类init里面的形参全是默认参数 需注意super所放的位置
        self.name = name  # 这里的self是进程对象 给新产生的进程对象新增两个对象独有的属性name、age
        self.age = age
        # super().__init__()

    def run(self):
        print('run is running', self.name, self.age)
        time.sleep(3)
        print('run is over', self.name, self.age)


if __name__ == '__main__':
    obj = MyProcess('jason', 123)
    obj.start()
    print('我是主进程的')

image

image

进程间数据隔离

同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)

from multiprocessing import Process
import time

money = 10000

def task():
    global money
    money = 888
    print('子进程的task函数查看money', money)

if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    time.sleep(3)
    print(money)

image

上面我们看到,在windows中创建进程是相当于导模块的操作,因此可以看成子进程的代码相当于在另外一个py文件中执行,虽然用上了global改变全局变量,因为跟主进程不在一个文件,可以看成产生了数据隔离

进程的join方法

from multiprocessing import Process
import time


def task(name):
    print('%s is running' % name)
    time.sleep(3)
    print('%s is over' % name)


if __name__ == '__main__':
    P = Process(target=task, args=('jason',))
    P.start()  # 异步
    P.join()   # 主进程代码等待子进程代码运行结束再执行
    print('我是主进程的')  # 这样主进程的永远都是最后打印
    
# 变形
from multiprocessing import Process
import time


def task(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('jason1', 1))
    p2 = Process(target=task, args=('jason2', 2))
    p3 = Process(target=task, args=('jason3', 3))
    start_time = time.time()
    p1.start()    # 主进程同时创建了3个子进程P1 P2 P3 在第一个P1进程执行时 时间为1秒 那么其他子进程P2 P3异步也执行了1秒 同理到在P2子进程时执行2秒 P3也执行了2秒了
    # p1.join()
    p2.start()
    # p2.join()
    p3.start()
    # p3.join()  # 按顺序的话就是一个个来轮流执行 时间6秒多
    p1.join()
    p2.join()
    p3.join()
    print(time.time() - start_time)  # 3秒多
    
打印结果:
    jason1 is running
    jason2 is running
    jason3 is running
    jason1 is over
    jason2 is over
    jason3 is over
    3.097902297973633

IPC机制

IPC:进程间通信
消息队列:储存数据的地方 所有人都可以存 也都可以取
from mutiprocessing import Queue

q = Queue(3)  # 括号内可以指定存储数据的个数
q.put(999)  # 往队列里存放数据
q.put(666)
q.put(888)
print(q.full())  #  True 判断队列是否已满

print(q.get())  # 999 从消息队列中取数据 按照先进先出取值

print(q.empty())  # Flase 判断队列里是否为空

from multiprocessing import Process,Queue
 def product(q):
        q.put('子进程获取队列中的数据', q.get())
        
 

image

full() empty() 在多进程中都不能使用


from multiprocessing import Process, Queue


def product(q):
    q.put('子进程p添加的数据')

def consumer(q):
    print('子进程获取队列中的数据', q.get())


if __name__ == '__main__':
    q = Queue()
    # 主进程往队列中添加数据
    # q.put('我是主进程添加的数据')
    p1 = Process(target=consumer, args=(q,))
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    print('我是主进程')

image

生产者 消费者模型

1.生产者

​ 负责生产数据的人

2.消费者

​ 负责处理数据的人

该模型除了有生产者和消费者之外还必须有消息队列(只有能够提供数据保存服务和提取服务的理论上都可以)

进程对象的多种方法

1.如何查看进程号
image

image

方式一
from multiprocessing import Process, current_process


def task():
    print(current_process())
    print(current_process().pid)  # 获取当前进程的进程


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    print(current_process())
    print(current_process().pid)  # 获取当前进程的进程
    
方式二
from multiprocessing import Process
import os

def task():
    print('我是子进程', os.getpid())  # 我是子进程 34604



if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    print('我是主进程', os.getpid())  # 我是主进程 40604
    print('我是pycharm进程编号', os.getppid)  # 我是pycharm进程编号 33528 获取主进程的父进程

image

2.终止进程

from multiprocessing import Process, current_process
import time

def task():
    print('子进程',current_process().pid)  # 获取当前进程的进程


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    # time.sleep(0.1)
    p.terminate()  # 终止p子进程  也是异步操作
    print('主进程', current_process().pid)  # 获取当前进程的进程

image
3.判断进程是否存活

from multiprocessing import Process, current_process
import time

def task():
    print('子进程',current_process().pid)  # 获取当前进程的进程


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    # p.terminate()
    print(p.is_alive())  # 判断子进程是否存活
    # time.sleep(0.1)
    # p.terminate()  # 终止p子进程  也是异步操作
    print('主进程', current_process().pid)  # 获取当前进程的进程

image

守护进程

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

  其二:守护进程内无法再开启子进程,否则抛出异常:

AssertionError: daemonic processes are not allowed to have children

注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

守护进程会随着守护的进程结束而立刻结束
	eg: 吴勇是张红的守护进程 一旦张红嗝屁了 吴勇立刻嗝屁
      
from multiprocessing import Process
import time


def task(name):
    print('德邦总管:%s' % name)
    time.sleep(3)
    print('德邦总管:%s' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('大张红',))
    p1.daemon = True
    p1.start()
    time.sleep(1)
    print('恕瑞玛皇帝:小吴勇嗝屁了')

僵尸进程与孤儿进程

僵尸进程
	进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来‘
    比如进程号、进程执行时间、进程消耗功率等给父进程查看
    ps:所有的进程都会变成僵尸进程
孤儿进程
	子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理

多进程数据错乱问题

模拟抢票软件

from multiprocessing import Process
import time
import json
import random


# 查票
def search(name):
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))


# 买票
def buy(name):
    # 再次确认票
    with open(r'data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    # 模拟网络延迟
    time.sleep(random.randint(1, 3))
    # 判断是否有票 有就买
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print('%s买票成功' % name)
    else:
        print('%s很倒霉 没有抢到票' % name)


def run(name):
    search(name)
    buy(name)


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=run, args=('用户%s'%i, ))
        p.start()
   
"""
多进程操作数据很可能会造成数据错乱>>>:互斥锁
	互斥锁
		将并发变成串行 牺牲了效率但是保障了数据的安全
"""
posted @ 2022-11-18 22:18  小福福  阅读(93)  评论(0编辑  收藏  举报