同步与异步
- 用来表达任务的提交方式
- 同步:提交完任务之后原地等待任务的返回结果 期间不做任何事
- 异步:提交完任务之后不原地等待任务的返回结果 直接去做其他事 有结果自动通知
阻塞与非阻塞
- 用来表达任务的执行状态
- 阻塞:阻塞态 堵车
- 非阻塞:就绪态 运行态
综合使用
- 同步阻塞
- 同步非阻塞
- 异步阻塞
- 异步非阻塞(效率最高)
创建进程的多种方式
1.鼠标双击软件图标
2.python代码创建进程
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=('jason',)) 位置参数
p1 = Process(target=task, kwargs={'name':'jason123'}) # 关键字参数
p1.start() # 异步 告诉操作系统创建一个新的进程 并在该进程中执行task函数
# task() # 同步
print('主')
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('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(3) # 主进程代码等待3秒 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))
# p.start() # 异步
主进程代码等待子进程代码运行结束再执行
# p.join()
# print('主')
start_time = time.time()
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
# p1.join()
# p2.join()
# p3.join()
print(time.time() - start_time) # 3秒多
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() # 主进程往队列中添加数据
生产者消费者模型
-
生产者:负责产生数据的'人'
-
消费者:负责处理数据的'人'
-
该模型除了有生产者和消费者之外还必须有消息队列(只要是能够提供数据保存服务和提取服务的理论上都可以)
进程对象的多种方法
1.如何查看进程号
from multiprocessing import Process, current_process
current_process()
current_process().pid
import os
os.getpid()
os.getppid()
2.终止进程
p1.terminate()
ps:计算机操作系统都有对应的命令可以直接杀死进程
3.判断进程是否存活
p1.is_alive()
4.start()
5.join()
守护进程
-
守护进程会随着守护的进程结束而立刻结束
吴勇是张红的守护进程 一旦张红嗝屁了 吴勇立刻嗝屁 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('恕瑞玛皇帝:小吴勇嗝屁了')
僵尸进程与孤儿进程
- 僵尸进程:进程执行完毕后并不会立刻销毁所有的数据 会有一些信息短暂保留下来;比如进程号、进程执行时间、进程消耗功率等给父进程查看;所有的进程都会变成僵尸进程
- 孤儿进程:子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣福利院管理
多进程数据错乱问题
模拟抢票软件
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() -
多进程操作数据很可能会造成数据错乱>>>:互斥锁
-
互斥锁:将并发变成串行 牺牲了效率但是保障了数据的安全