内容概要

  • 同步与异步

  • 阻塞与非阻塞

  • 创建进程的多种方式

  • 进程的join方法

  • 进程间数据隔离

  • 进程间通信之IPC机制

同步与异步

用来表达任务的提交方式

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

阻塞与非阻塞

用来表达任务的执行状态

阻塞
	阻塞态
非阻塞
	就绪态、运行态

综合使用

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

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

创建进程的多种方式

""" 
1.创建进程的方式
    鼠标双击桌面一个应用图标
    代码创建
    
创建进程的本质:
     在内存中要一块内存空间用来运行相应的程序代码
    """
# multiprocessing 进程模块
# Process P是大写
from multiprocessing import Process
import time

# 用函数创建进程
def task(name):
    print('task is running', name)
    time.sleep(3)
    print('task is over', name)
    
"""
在不同的操作系统中创建进程底层原理不一样
    windows
        以导入模块的形式创建进程
    linux/mac
        以拷贝代码的形式创建进程
"""

if __name__ == '__main__':
    p1 = Process(target=task,args=('superman',)) # 位置参数
    # p1 = Process(target=task, kwargs={'name': 'superman'})  # 关键字参数
    p1.start()  # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
    print('主进程')

image

# 面向对象创建进程
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(3)
        print('run is over', self.name, self.age)


if __name__ == '__main__':
    obj = MyProcess('superman', 18)
    obj.start()
    print('主进程')

image

进程间数据默认隔离

同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)
# 我们可以把内存看成很对个小房间组成的, 一人一个互不干扰
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(3)  # 主进程代码等待3秒
    print(money)  # 主进程代码打印money
# 同一台计算机上面,进程和进程之间是数据隔离的

image

进程的join方法

# 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=('super xz1', 1))
    p2 = Process(target=task, args=('super xz2', 2))
    p3 = Process(target=task, args=('super xz3', 3))
    '''主进程代码等待子进程代码运行结束再执行'''
    start_time = time.time()
    p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()
    end_time = time.time() - start_time
    print('主进程', f'总耗时:{end_time}')

image

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

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 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('主')

生产者消费者模型

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

进程对象属性和方法

"""
 查看进程号的方法:
     windows: tasklist 结果集中PID
     mac:  ps -ef
"""

1.1、查看进程号
  courrent_process函数
  from multiprocessing import Process, current_process
print(current_process().pid)  # 13236

# 获取进程号的目的就是可以通过代码的方式管理进程
windows系统: taskkill关键字
Mac/linux系统: kill关键字

1.2、os模块
  os.getpid()  # 获取当前进程的进程号
  os.getppid() # 获取到当前进程的父进程号

2.杀死子进程
  terminate()   # 这个过程可能需要一点时间
3.判断子进程是否存活
  is_alive()  # 返回True或者False

守护进程

"""
  守护就是生死全靠守护的对象,对生守生, 对死守死
"""

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=('黑大帅', ))
    p.daemon = True  # 将子进程设置为守护进程,主进程结束,子进程立即结束
    p.start()
    print('皇上毙了')
# p.daemon = True 一定要放在p.start()上面

僵尸进程与孤儿进程

僵尸进程
   所有的子进程在运行结束之后都会编程僵尸进程(死了没死透)
   因为还保留着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()
        
用户:2查询余额为:2
用户:1查询余额为:2
用户:3查询余额为:2
用户:5查询余额为:2
用户:4查询余额为:2
用户:4抢票成功用户:5抢票成功

用户:2抢票成功
用户:1抢票成功
用户:3抢票成功
#  结果很明显,是错乱的,而且都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查询余额为:2
用户:2查询余额为:2
用户:3查询余额为:2
用户:4查询余额为:2
用户:5查询余额为:2
用户:1抢票成功
用户:2抢票成功
用户:3抢票失败,票已经没了
用户:4抢票失败,票已经没了
用户:5抢票失败,票已经没了
    
    
"""
  行锁: 针对行数加锁, 同一时间只能一个人操作
  表锁: 针对表数据加锁, 同一时间只能一个人操作
"""

原文地址:http://www.cnblogs.com/super-xz/p/16905051.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性