1.同步与异步
1.同步异步用来表达任务的提交方式
2.同步:同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去。
'''代码执行到某行时,如果代码需要调用其他代码,那么就去其他文件执行代码,执行完之后再回来'''
3.异步:异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据。
'''代码执行到某行时,如果代码需要调用其他代码,继续往下执行'''
"""
你和朋友去吃饭,你们到达饭店,点了一桌满汉全席。由于你们饿的不行,就在饭店等待厨师做好菜,等菜上桌。你和你的朋友吃完饭,付了钱,舒舒服服的去商场购物了,一次饭局就结束了。这就是同步调用。
如今,我们正处在互联网时代,当你们饿的时候,就打开饿了么,点了一桌满汉全席,支付了餐费,这时你就完成了点菜。商家接到了你的订单之后,就会马上开始安排(毕竟顾客就是上帝嘛)。现在你们无须默默等待,以免被饿所折磨,于是你们打开了京东,开始了新的购物。当饿了么小哥饭菜送到时,一签单就完事了,并且你们也完成了购物。这就是异步调用。
我们之前所写的代码都是同步调用。
"""
2.阻塞与非阻塞
阻塞与非阻塞表达任务的执行状态
1.阻塞:任务处于阻塞态
2.非阻塞:任务处于就绪态、运行态
3.综合使用
我们通常不会只看到上述单个词汇,而是会看到上述词汇的组合使用,含义举例说明:
1.同步阻塞:只运行这一个程序,提交任务之后,等待CPU回来继续运行,效率最低。
'''去银行排队,专心排队其他什么事情都不做'''
2.同步非阻塞:只运行这一个程序,提交完任务之后,可以原地做一些其他的事情。
'''还是用普通的汤锅,米放进去,然后去打游戏,过一会就来看一次 '''
3.异步阻塞:程序处于阻塞态,但是可以做一些其他操作。
'''去某宝淘了个电饭锅,饭熟了会自动跳闸的那种。米放进去,然后傻傻的看它怎么做饭的'''
4.异步非阻塞:提交完任务之后CPU马上去运行另一个程序,并且处于运行态或者就绪态,异步非阻塞效率最高。
'''米放进去,按下按钮,然后就去打游戏了,等到跳闸的时候就去吃饭。'''
4.创建进程的多种方式
1.两种方式:
1.1 鼠标双击软件图标
1.2 python代码
2.用python代码创建进程(方法一):
"""
不同的操作系统盘创建进程的底层原理不一样
windows:以导入模块的形式创建进程
linux:以拷贝代码的形式创建进程
"""
windows如果用代码创建进程,进程文件相当于导模块,会将原来的代码原封不动地拷贝一份到新进程,新进程执行到创建进程那一步会继续创建新进程,往复循环,直到报错。而苹果电脑创建的新进程会不包含创建新进程的那一步,所以苹果电脑创建新进程不会循环报错。windows代码及报错信息如下:
from multiprocessing import Process
import time
def task(name):
print('task is running', name)
time.sleep(3)
print('task is over', name)
p1 = Process(target=task)
p1.start()
print('主进程')
![](/i/l/?n=22&i=blog/2987409/202211/2987409-20221118222652639-1068542049.png)
"""
windows创建新进程是以导模块的形式创建,所以导模块的几步应该要加上验证是否是执行文件的:if __name__ == '__main__'。
"""
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=(111,)) # 创建一个子进程对象
p1.start() # 用子进程对象创建一个新的进程,并在该进程中执行task功能
print('主进程') # 执行结果:主进程 task is running 111 task is over 111
说明p1.start()生成一个新进程是一个异步操作,子进程中调用task函数,因为子进程拷贝代码需要时间,所以父进程中的print('主进程')会优先打印,打印结束之后会再执行子进程。
from multiprocessing import Process
import time
def task(name):
print('task is running', name)
time.sleep(3)
print('task is over', name)
def func1():
print('from func1')
if __name__ == '__main__':
p1 = Process(target=task,args=(111,))
p2 = Process(target=func1)
p1.start()
p2.start()
print('主进程')
# 执行结果:主进程 task is running 111 from func1 task is over 111
一个父进程可以同时开多个子进程,子进程P1在读取的过程中先执行print('主进程'),再执行子进程p1中的print('task is running', name),此时子进程p1中还有部分代码未执行,由于要sleep3秒,此时先执行p2中的print('from func1'),最后执行print('task is over', name)。
3.用python代码创建进程(方法二):
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('max', 25)
obj.start()
print('主进程')
# 主进程 run is running max 25 run is over max 25
该方法是利用类产生对象,然后用对象产生子进程的过程。还是先执行父进程中的print('主进程'),再执行子进程中的run is running max 25、run is over max 25。
5.进程的join方法、数据隔离
'''同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)'''
money = 1000
def task():
money = 666
if __name__ == '__main__':
task()
print(money)
# 1000 print(money)找的是全局名称空间中的money
money = 1000
def task():
global money
money = 666
if __name__ == '__main__':
task()
print(money)
# 666 global局部修改全局,全局名称空间中的money被改成666
from multiprocessing import Process
import time
money = 1000
def task():
global money
money = 666
print('子进程中的money', money) # 子进程中的money 666
if __name__ == '__main__':
p1 = Process(target=task)
p1.start()
time.sleep(3)
print(money)
# 1000
"""
如果没有if __name__ == '__main__':及子代码,此时全局名称空间中的money已经被修改成666,局部名称空间中无money。但是要生成一个子进程,在子进程中调用task()函数,子进程中会调用全局名称空间中的money以及task()函数,task()函数执行修改的是子进程中全局名称空间中的money。task()函数体代码中查看的是子进程中的money,结果是666,说明已经修改。
"""
需求:如何实现子进程运行结束之后再运行主进程?
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=('max',))
p.start()
print('主进程')
# 主进程 max is running max is over
"""
正常运行会先执行父进程的所有代码,再执行子代码,因为在子代码读取过程中CPU会先读取父代码并且执行
"""
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=('max',))
p.start()
time.sleep(4)
print('主进程')
# max is running max is over 主进程
"""
上述代码实现了先执行子进程再执行父进程,依靠的是父进程休眠时间总是比子进程长一些。但这种方法的弊端在于我们不知道子进程什么时候会休眠几秒,使用这种方式存在很多不确定性
"""
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=('max',))
p.start()
p.join()
print('主进程')
# max is running max is over 主进程
"""
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=('max1',1))
p2 = Process(target=task, args=('max2',2))
p3 = Process(target=task, args=('max3', 3))
start_time = time.time()
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
print(time.time() - start_time)
"""
max1 is running
max2 is running
max3 is running
max1 is over
max2 is over
max3 is over
3.151333808898926
"""
"""
根据前六部打印顺序得知,第一个子进程先开始,不等睡眠结束就开始运行第二个,再第三个。start()连在一起说明无需等待上一个子进程结束就可以开始下一个。
"""
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=('max1',1))
p2 = Process(target=task, args=('max2',2))
p3 = Process(target=task, args=('max3', 3))
start_time = time.time()
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
print(time.time() - start_time)
"""
max1 is running
max1 is over
max2 is running
max2 is over
max3 is running
max3 is over
6.363624334335327
"""
"""
join()表示必须等子进程运行结束之后才能运行主进程,同时也表示上一个子进程结束之后才能运行下一个。
"""
6.IPC机制
IPC机制:同一台电脑上进程间通信
消息队列:存储数据的地方,所有人都可以存,也可以取,类似于一个仓库
"""
知识回顾:队列:先进先出;栈:先进后出
"""
from multiprocessing import Queue
q = Queue(3)
q.put(11)
print(q.full()) # False 判断队列是否已满
q.put(22)
q.put(33)
print(q.full()) # True
# q.put(44) # 数量超过存放数量,不会报错,但是不会再执行下面的代码了,想要继续执行要把这一行注掉
print(q.get()) # 11
print(q.get()) # 22
print(q.empty()) # False 判断列表是否为空
print(q.get())
print(q.get_nowait()) # 拿完了,用get_nowait()继续拿会直接报错
"""
full(), empty()在多进程中不能使用,因为判断的上一刻列表数据可能就发生变化。聊天软件中的消息即使不上线也可以收到,因为消息存放在消息队列中。
"""
7.进程中通信
上述知识点我们得知了队列可以当做一个仓库来存放数据,那么队列是否可以当做不同进程之间数据联系的仓库呢?
from multiprocessing import Process, Queue
def consumer(q):
'''在子进程中拿出数据'''
print('子进程从队列当中拿到的数据:', q.get())
if __name__ == '__main__':
q = Queue()
'''在主进程中添加数据'''
q.put('主进程添加的数据a')
p1 = Process(target=consumer, args=(q,))
p1.start()
p1.join()
print('主进程')
'''
子进程从队列当中拿到的数据: 主进程添加的数据a
主进程
'''
"""
上述代码我们得出结论:主进程和子进程可以依靠队列来进行数据交互
"""
from multiprocessing import Process, Queue
def product(q):
q.put('子进程p2添加进队列的数据a')
def consumer(q):
print('子进程p1从队列中拿到的数据:', q.get())
if __name__ == '__main__':
q = Queue()
p1 = Process(target=consumer, args=(q,))
p2 = Process(target=product, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
print('主进程')
'''
子进程p1从队列中拿到的数据: 子进程p2添加进队列的数据a
主进程
'''
"""
不仅子进程和主进程之间可以通过队列数据交互,子进程自检也可以进行数据交互
"""
8.生产者消费者模型(主要用在爬虫领域)
1.生产者:负责生产数据的'人';eg:爬虫爬取到的数据写入excel的过程。
2.消费者:负责处理数据的'人'。
"""
该模型除了上述两种类型,还应该有消息队列,生产者和消费者无直接关联,消息队列可以让生产者和消费者产生关联,并且可以对生产者产生的数据进行存储,也避免了两者需要同时工作的现象。消息队列也可以是数据库,文件,只要是能提供保存和提取服务的都可以
"""
9.进程对象的多种方法
1.在cmd窗口输入tasklist能看到系统给每一个进程分配的进程号,进程号和端口号一样都是为了方便管理
from multiprocessing import Process, current_process
def task():
print(current_process())
print(current_process().pid) # 获取子进程进程号
if __name__ == '__main__':
print(current_process())
print(current_process().pid) # 获取主进程进程号
p1 = Process(target=task)
p1.start()
p1.join()
print('主进程')
'''
<_MainProcess(MainProcess, started)>
48692
<Process(Process-1, started)>
49216
主进程
'''
2.获取进程号也可以选用os模块中的os.getpid()方法,os.getppid()是查看父进程的进程号,其中子进程的父进程号和主进程号一致,因为主进程就是子进程的父进程。主进程的父进程是pycharm的进程号。
import os
from multiprocessing import Process
def task():
print('子进程:', os.getpid())
print('子进程的父进程', os.getppid())
if __name__ == '__main__':
p1 = Process(target=task)
p1.start()
p1.join()
print('主进程', os.getpid())
print('主进程的父进程', os.getppid())
'''
子进程: 47784
子进程的父进程 46788
主进程 46788
主进程的父进程 2976
'''
![](/i/l/?n=22&i=blog/2987409/202211/2987409-20221118222822217-2038674557.png)
3.杀死进程:在pycharm中可以用子进程对象.terminate()结束。
import os
from multiprocessing import Process
def task():
print('子进程:', os.getpid())
print('子进程的父进程', os.getppid())
if __name__ == '__main__':
p1 = Process(target=task)
p1.start()
p1.terminate()
print('主进程', os.getpid())
print('主进程的父进程', os.getppid())
'''
子进程: 47784
子进程的父进程 46788
主进程 46788
主进程的父进程 2976
'''
"""
也可以在cmd窗口用taskkill /F /PID 进程号 来终止
"""
# 4
![](/i/l/?n=22&i=blog/2987409/202211/2987409-20221118222535071-1230317037.png)
4.判断进程对象时候出存活:
def task():
print('子进程:', os.getpid())
if __name__ == '__main__':
p1 = Process(target=task)
p1.start()
print(p1.is_alive()) # True
p1.terminate()
print(p1.is_alive()) # True
"""
由于终止后程序还在运行,只需要让它睡一下,就会发现已经不再存活
"""
import os
from multiprocessing import Process
import time
def task():
print('子进程:', os.getpid())
if __name__ == '__main__':
p1 = Process(target=task)
p1.start()
print(p1.is_alive()) # True
p1.terminate()
time.sleep(1)
print(p1.is_alive()) # False
10.守护进程
进程如果有守护进程,它关闭了那么它的守护进程也关闭
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=('进程1',))
p1.daemon = True # p1进程是主进程的守护进程
p1.start()
time.sleep(1)
print('主进程') # 主进程执行完毕,守护进程还没执行完,就已经结束
'''
进程1是子进程
主进程
'''
11.僵尸进程与孤儿进程
1.僵尸进程:进程执行完毕后并不会立刻销毁所有的数据,会有一些信息短暂保留下来,比如进程号、进程执行时间、进程消耗功率等给父进程查看
ps:所有的进程都会变成僵尸进程
2.孤儿进程:子进程正常运行,父进程意外死亡,操作系统针对孤儿进程会派遣福利院管理。
12.多进程数据错乱问题
模拟抢票软件
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()
"""
多进程操作数据很可能会造成数据错乱>>>:互斥锁
互斥锁
将并发变成串行 牺牲了效率但是保障了数据的安全
"""
标签:__,异步,同步,name,p1,阻塞,print,task,进程
From: https://www.cnblogs.com/zkz0206/p/16905083.html