python之路31 网络并发编程方法 同步异步 阻塞非阻塞 进程join方法 IPC机制 守护进程 创建进程的两种方式

image

同步与异步

用来表达任务的提交方式
同步
   提交完任务之后原地等待任务的返回结果 期间不做任何事
异步
   提交完任务之后不原地等待任务的返回结果 直接去做其他事 有结果自动通知

阻塞与非阻塞

用来表达任务的执行状态
阻塞
   阻塞态
非阻塞
   就绪态、运行态

总结

同步阻塞
同步非阻塞
异步阻塞
异步非阻塞(******)
    效率最高

同步、异步,与阻塞、非阻塞不相关。
同步、异步强调的是结果。
阻塞和非阻塞强调的是时间,是否等待。
同步与异步区别在于:调用者是否得到了想要的最终结果。

同步就是一直要执行到返回最终结果。

异步就是直接返回了,但是返回的不是最终的结果,调用者不能通过这种调用得到结果,还要通过被调用者,使用其他方式通知调用者,来取回最终结果。

 

阻塞与非阻塞的区别在于,调用者是否还能干其他的事情。

阻塞,调用者只能干等。

非阻塞,调用者可以先忙一会别的,不用一直等。

 

联系:

同步阻塞:调用者阻塞,直到等到拿到最终结果。(打饭模型,什么事情也不敢,就等着打饭,打饭是结果,什么也不干,一直在等着,同步加阻塞)

同步非阻塞:(等着打饭,但是可以玩会手机,看看电视,打饭是结果,但是不用一直在等着)

异步阻塞:(我要打饭,你说等着较好,并没有返回饭给我,我啥事不干,就干等着饭好了叫我)

异步非阻塞:回调的话。(我要打饭,你说等较好,并没有返回饭给我,可以在旁边看看电视,玩玩手机,饭好了叫我)



同步IO、异步IO、IO多路复用

IO模型:

IO分为两个阶段。

1)数据准备阶段。

2)内核空间复制回用户进程缓冲区阶段。

发生IO的时候:

1、内核从输入设备读、写数据(淘米,把米放锅里煮饭)

2、进程从内核复制数据(盛饭,从内核这个锅把饭装到碗里面来)
 

创建进程的两种方式

"""
1.鼠标双击软件图标
2.python代码创建进程
"""
1、利用函数创建
from multiprocessing import Process
import time

def task(name):
    print('task is running',name)
    time.sleep(1)
    print('task is over', name)
if __name__ == '__main__':
    p1 = Process(target=task,args=('jason',))  # 位置参数
    # p1 = Process(target=task,kwargs={'name':'jason123'})  # 关键字参数
    p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
    # task()  # 同步
    print('主程序')
"""
在不同的操作系统中 创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""

2、利用类创建
from multiprocessing import Process
import time

class MyProcess(Process):
    def __init__(self,name,age):
        super().__init__()
        self.name = name
        self.age = age

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

if __name__ == '__main__':
    obj = MyProcess('jason',123)
    obj.start()
    print('主程序')

进程间数据间隔

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

money = 1000

def task():
    global money
    money = 666
    print('子进程的task函数产看money', money)
if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()  # 创建子进程
    time.sleep(2)  # 主进程代码等待2秒
    print(money)    # 主进程代码打印money

进程的join方法

主进程代码等待子进程代码执行完毕 再执行主进程代码


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))
    # p1.start()  # 异步
    # p1.join()
    # print('主程序')
    stat_time = time.time()
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    # p1.join()
    # p2.join()
    # p3.join()
    print(time.time() - stat_time)  #  6秒多

IPC机制

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

q = Queue(3)   # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
print(q.full())  #  判断消息队列是否已满
q.put(222)
q.put(333)
print(q.full())     # 判断消息队列是否已满
# 从消息队列中取数据
print(q.get())
print(q.get())
print(q.empty())   # 判断队列是否为空
print(q.get())
print(q.empty())  # 判断队列是否为空
print(q.get())  #  取数据    无数据会原地等待
# print(q.get_nowait())     #  也是取数据 但是如果队列里面没有数据会报错

'''full()  empty()  在多进程中都不能使用!!!!'''
from multiprocessing import Process, Queue

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

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

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

生产者消费者模型

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



生产者消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

进程对象的多种方法

1.如何查看进程号
    from multiprocessing import Process,current_process
    current_process()
    print(current_process().pid)
    import os

    print(os.getpid())
    print(os.getppid())
2.终止进程
    p1.terminate()
    ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
    p1.is_alive()
5.start()  创建进程
6.join()     阻塞等待进程

守护进程

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

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

if __name__ == '__main__':
    p1 = Process(target=task, args=('B',))
    p1.daemon = True
    p1.start()
    time.sleep(1)
    print('恕瑞玛皇帝: A嗝屁了')

僵尸进程与孤儿进程

僵尸进程
    进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来
    比如进程号、进程执行时间、进程消耗功率等给父进程查看
    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.randrange(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(1,11):
        p = Process(target=run, args=('用户%s' % i,))
        p.start()

"""
多进程操作数据很肯会造成数据错乱>>>:互斥锁
     互斥锁
          将并发变成串行 牺牲了效率但是保障了数据的安全
"""


# 加锁之后的代码


from multiprocessing import Process,Lock
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:
            with open(r'data.json', 'w',encoding='utf8')as f:
                data['ticket_num'] -= 1
                json.dump(data,f)
            print('%s 买票成功' % name)
        else:
            print('%s 买票失败 非常可乐  没车回去了'% name)
def run(name,mutex):
    search(name)
    mutex.acquire()  # 抢锁
    buy(name)
    mutex.release()  # 释放锁

if __name__ == '__main__':
    mutex = Lock()    # 产生一把锁
    for i in range(10):
        p = Process(target=run, args=('用户%s号' % i,mutex))
        p.start()

"""
锁有很多种  但是作用都一样
    行锁 表锁。。。。
"""
posted @ 2022-11-18 16:06  缀月  阅读(224)  评论(0编辑  收藏  举报