同步异步/阻塞非阻塞,进程的多种方法

同步与异步

表达任务的提交方式
同步:
	提交完任务之后会在原地等待任务的返回结果,在等待的过程不会做任何事。
异步:
	提交完任务之后 不原地等待,去干别的事情,有结果自动通知。

阻塞与非阻塞

任务的执行状态
阻塞态
	进程的三状态中的阻塞态。任务有IO操作时就会进入
非阻塞态
   就绪态和运行态
    
 """
    如果想要提高程序被执行效率
       就要程序一直处于就绪态和运行态
  """

同步异步和阻塞费阻塞结合使用

同步异步:用来描述任务的提交方式
阻塞非阻塞: 用来描述任务的执行状态

同步+阻塞:
  银行排队办理业务,期间不做任何事,就等着
同步+非阻塞:
  银行排队办理业务,期间可以去做一些其他的事,但是人还在办理业务的队列中
    
异步阻塞:
   在椅子上坐着,不做任何事
异步非阻塞:
   在椅子上面坐着,在期间喝水、吃东西、工作、玩手机、、(这个过程就是把程序运行到了极致) 

创建进程的多种方式

我们常用的就是直接双击软件图标。
但是python代码也给我们提供了创建进程的方式

multiprocessing 模块

import time
from multiprocessing import Process

第一种方法:

def task(name):
    print('子程序开始', name)
    time.sleep(3)
    print('子程序结束', name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('tank',))  
    p1.start()	# 异步     
    task()  # 同步
    print('这是主程序')
===================================================================================

第二种方法:
class MyProcess(Process):
    import time
from multiprocessing import Process


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

    def run(self):
        print('子程序开始',self.name)
        time.sleep(3)
        print('子程序结束',self.age)

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

进程间的数据隔离

一台计算机上的多个进程 数据在默认情况下是无法访问的。也称为物理隔离,但是我们也可以通过 global 来修改 外部数据实现
from multiprocessing import Process
import time

name = '呆子'


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


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()  # 创建子进程
    time.sleep(3)  # 主进程代码等待3秒
    print(name)  # 主进程代码打印name
"在子程序中复制了同样的代码形成了一个新的内存空间,在此内存空间的task函数类利用global修改了外部name的值"

"""
在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""

进程的join方法

"join方法就是让主进程等待子进程代码运行完毕之后 再执行后续的代码"
from multiprocessing import Process
import time



def task(name,username,n):
    print(f'子进程的{name}开始打{username}')
    time.sleep(n)
    print(f'子进程的{username}被打{name}打进了医院')

if __name__ == '__main__':
    p1 = Process(target = task, args = ('李晓健','周衍根',2))
    p2 = Process(target = task, args = ('李晓健','周衍根',2))
    start_time = time.time()
    p1.start()
    p1.join()  # 等待p1 子进程执行完毕后在执行下面的代码
    p2.start()
    p2.join()
    print(time.time() - start_time)

    
    想要p1.start()之后的代码, 等待子进程全部运行结束之后再打印
   1.直接sleep,但是这个方法肯定不可行,因为子进程运行时间不可控
    2.join方法
      针对多个子进程的等待

IPC机制与消息队列

IPC : 进程间相互通信
消息队列:存储数据的地方,谁都能存,谁都能取
Queue 
from multiprocessing import Process,Queue
import time



def start(q):
    q.put('我是子进程1,我往消息队列里添加了 “我是你哥”')

def end(q):
    time.sleep(3)
    print(f'子进程2获取数据',q.get(),q.get())
if __name__ == '__main__':
    q = Queue()   # 括号内可以设置存放消息的数量
    # 主进程也可以往队列里添加数据
    q.put('我是你们的父亲')
    p1 = Process(target=start,args=(q,))
    p2 = Process(target=end,args = (q,))
    p1.start()
    p2.start()
----------------------------------------
子进程2获取数据 我是你们的父亲 我是子进程1,我往消息队列里添加了 “我是你哥”
----------------------------------------
# 可以看出 消息队列先进先出的规则。
	 

生产者消费者模型

我们的代码里都有生产者与消费者
生产者:
	负责产生数据 
消费者:
	负责处理数据
该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以) 
eg: 文件, TXT ,数据库

进程对象的多种方法

1.查看进程号:
 	current_process()    # 查看父进程号
 	current_process().pid  # 查看子进程号
os查看进程号:
    import os
 	os.getpid()         # 查看子进程号
  	os.getppid()        # 查看父进程号
代码:
def start():
    print(f'我是子进程1我的进程号:', os.getpid())
    print(f'我是子进程1我的父进程号:', os.getppid())
def end():
    print(f'我是子进程2我的进程号:', os.getpid())
    print(f'我是子进程2我的父进程号:', os.getppid())
if __name__ == '__main__':
    p1 = Process(target=start)
    p2 = Process(target=end)
    p1.start()
    p2.start()
----------------------------------------
我是子进程1我的进程号: 45084
我是子进程2我的进程号: 22744
我是子进程1我的父进程号: 28576
我是子进程2我的父进程号: 28576
----------------------------------------
===================================================================================
2.中止进程:
	p1.terminate()  
    
代码:
def start():
    print(f'我是子进程1')
def end():
    print(f'我是子进程2')
if __name__ == '__main__':
    p1 = Process(target=start)
    p2 = Process(target=end)
    p1.start()
    p1.terminate()
    print('我p1被杀死了')
    p2.start()
----------------------------------------
我p1被杀死了
我是子进程2
----------------------------------------
===================================================================================
3.判断进程是否存活
p1.is_alive() # 判断过程中会经历主进程回收,如果中间不加 sleep  还是 为True 还没死透,也称为假死
4.start()   # 创建子进程
5.join()	# 等待子进程代码执行完毕后在执行主进程代码

守护进程

守护进程会随着守护的   进程结束(主进程)而立刻结束
p.daemon = True

from multiprocessing import Process
import time

def task(name):
    print(f'御用特使:{name}还没死')
    time.sleep(3)
    print(f'御用特使:{name}死了')

if __name__ == '__main__':
    p = Process(target=task, args=('黑大帅', ))
    # start_time = time.time()  
    p.daemon = True  # 将子进程设置为守护进程,主进程结束,子进程立即结束
    p.start()
    time.sleep(1)
    # end_time = time.time() - start_time
    print(f'我活了{end_time}')
    print('皇上毙了')
    print('我黑子也得死')
# p.daemon = True 一定要放在p.start()上面
----------------------------------------
御用特使:黑大帅还没死
我活了1.0227680206298828
皇上毙了
我黑子也得死
----------------------------------------

僵尸进程和孤儿进程

僵尸进程
   所有的子进程在运行结束之后都会变成僵尸进程(死了没死透)
   因为还保留着pid和一些运行过程的中的记录便于主进程查看(只是短时间保存)
    等这些信息被主进程回收,就彻底死了
    1.主进程正常结束
    2.调用join方法   等待子进程 死透
    # 僵尸进程是无害的
孤儿进程
  # 子进程存在,但是父进程毙了
  子进程会被操作系统自动接管   (类似于福利院收养了)

多进程数据错乱问题

import json
from multiprocessing import Process
import time
import random

# 设置票数,一张  是个文件
ticket_data.json : {"ticket_num": 1}
# 查票功能
def search(name):
    with open(r'ticket_data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    print(f'{name}查询余票为:%s'% data.get('ticket_num'))

# 买票功能
def buy(name):
    # 先查一下票数
    with open(r'ticket_data.json', 'r', encoding='utf8') as f:
        data = json.load(f)
    time.sleep(random.randint(1, 3))  # 停留1~3秒
    # 判断
    if data.get('ticket_num') > 0:
        data['ticket_num'] -= 1
        with open(r'ticket_data.json', 'w', encoding='utf8') as f:
            json.dump(data, f)
        print(f'{name}抢票成功')
    else:
        print(f'{name}抢票失败,票已经没了')

# 将买票封装成函数
def run(name):
    search(name)
    buy(name)

# 模拟多人同时
if __name__ == '__main__':
    for i in range(1, 6):
        p = Process(target=run, args=('用户:%s' % i, ))
        p.start()
        
用户:1查询余票为:1
用户:3查询余票为:1
用户:2查询余票为:1
用户:4查询余票为:1
用户:5查询余票为:1
用户:1抢票成功
用户:2抢票成功
用户:5抢票成功
用户:3抢票成功
用户:4抢票成功

#  结果很明显,是错乱的,而且都5个人买一张都成功了
"""
  在多个进程操作同一个数据的时候会造成数据的错乱, 所以我们需要增加一个加锁处理(互斥锁)
  将并发变成串行, 效率低了,但安全性高了
  互斥锁并不能轻易使用, 容易造成死锁现象
  互斥锁旨在处理数据的部分加锁, 不能什么地方都加

"""

加上互斥锁

# 增加互斥锁
from multiprocessing import Process, Lock
mutex = Lock()
mutex.acquire()  # 上锁
mutex.release()  # 解锁

# 将买票封装成函数
def run(name,mutex):
    search(name)
    mutex.acquire()  # 上锁
    buy(name)
    mutex.release()  # 解锁

# 模拟多人同时
if __name__ == '__main__':
    mutex = Lock()
    for i in range(1, 6):
        p = Process(target=run, args=('用户:%s' % i, mutex))
        p.start()


用户:1查询余票为:1
用户:2查询余票为:1
用户:3查询余票为:1
用户:4查询余票为:1
用户:5查询余票为:1
用户:1抢票成功
用户:2抢票失败,票已经没了
用户:3抢票失败,票已经没了
用户:4抢票失败,票已经没了
用户:5抢票失败,票已经没了


    
"""
  行锁: 针对行数加锁, 同一时间只能一个人操作
  表锁: 针对表数据加锁, 同一时间只能一个人操作
"""
posted @ 2022-11-18 20:32  李阿鸡  阅读(48)  评论(0编辑  收藏  举报
Title