一、同步与异步、阻塞与非阻塞
1.同步与异步介绍
一种方式,可以用来表示提交任务方提交任务后的行为
同步:好比去办车牌的时候,提交了资料就呆在大厅一动不动,等着审核结果出来
异步:好比去办车牌的时候,提交了资料后就留一个人在大厅等结果,另一个人出门去买水之类的
2.阻塞与非阻塞
其实也就是我们昨天学的阻塞态,意思就是任务卡住了
阻塞: 阻塞态
非阻塞:就绪态、运行态
3.搭配使用
主要看cpu还在不在身边,自己是不是呆着不动
同步阻塞、同步非阻塞
前者啥都干不了,后者还能干些事情但是不能离开大厅
异步阻塞、异步非阻塞
前者能干自己的事,也会有人通知结果是否出来,但是不能离开大厅去忙别的事
后者最有用,效率最高
二、代码的方式创建多进程
前言:我们下的程序,一般都是可以打开多个的,每打开一个,就是打开了一个新进程
multiprocessing模块
核心模块:from multiprocessing import Process
# 代码实操
from multiprocessing import Process
import time
# 随便创一个函数打点东西
def a():
print('任务正在执行')
time.sleep(5) # 手动阻塞一下
print('任务执行完毕')
# 因为执行程序就是相当于复制一份拿到内存里去执行,为了防止反复导,我们在代码中加上main的主体代码
if __name__ == '__main__':
p1 = Process(target=a) # 创一个程序对象
p1.start() # 创建新进程
print('主程序执行了')
>>>
主程序执行了
任务正在执行
任务执行完毕
————————————————————————————
注意,我们通过运行上面代码发现,主进程的print先打印,而子进程的print后打印,这是因为,当走到主进程创建子进程这一步,立马在内存空间中创建了一个子进程开始执行,但是同时主进程和子进程是异步操作,在子进程生效前主不会停下等子,所以主这边先打印,我们可以主动阻塞一下主进程,阻塞0.05s 、0.1s、1s都可以试试,发现确实子进程有一个生效时间
# 其他代码不变
if __name__ == '__main__':
p1 = Process(target=a) # 创一个程序对象
p1.start() # 创建新进程
time.sleep(0.05)
print('主程序执行了')
>>>
主程序执行了
任务正在执行
任务执行完毕 # 发现第一句第二句出来的很快,但是依旧主先打
——————————————————————————————————————————
# 其他代码不变
if __name__ == '__main__':
p1 = Process(target=a) # 创一个程序对象
p1.start() # 创建新进程
time.sleep(0.1)
print('主程序执行了')
>>>
任务正在执行
主程序执行了
任务执行完毕 # 可以看到,子进程先打了,主被超车了
# 所以在该案例中大概有0.1s的时间子进程生效
传参形式的子进程
# 代码实操
from multiprocessing import Process
import time
# 创一个函数打点东西,但是需要传参
def a(name):
print(f'{name}正在a进程执行任务')
time.sleep(3) # 手动阻塞一下
print(f'{name}a进程任务执行完毕')
# 因为执行程序就是相当于复制一份拿到内存里去执行,为了防止反复导,我们在代码中加上main的主体代码
if __name__ == '__main__':
p1 = Process(target=a, args=('jason',)) # 创一个程序对象,传参一定注意传参是框元组,逗号别省略!!!
p1.start() # 创建新进程
print('主程序执行了')
>>>
主程序执行了
jason正在a进程执行任务
jasona进程任务执行完毕
玩玩类的创建进程
# 代码实操
from multiprocessing import Process
import time
# 创一个函数打点东西,但是需要传参
class A(Process):
def __init__(self, name):
super().__init__() # super()看你想如何继承,是后修改还是先修改
self.name = name
def run(self):
print(f'{self.name}正在执行任务')
time.sleep(3) # 手动阻塞一下
print(f'{self.name}任务执行完毕')
# 因为执行程序就是相当于复制一份拿到内存里去执行,为了防止反复导,我们在代码中加上main的主体代码
if __name__ == '__main__':
obj = A('jason') # 新建一个对象
obj.start() # 创建新进程
print('主程序执行了')
三、进程的方法
- 进程间的数据是隔离的(默认情况下),具体看下面例子中的打印变量名c
1.join方法
等待子进程执行完后再执行主进程代码
from multiprocessing import Process
import time
from multiprocessing import Queue
c = 1000
def a():
print('a在执行')
global c
c = 666
print('子进程内>>>:', c)
time.sleep(3)
if __name__ == '__main__':
p1 = Process(target=a)
p1.start() # 告诉操作系统创建一个新的进程,并执行
p1.join() # 主进程代码等待子进程代码运行结束再执行
print('主在执行')
print(c) # 这个打印c只是打印主进程中的c,就算子进程改了主进程的依旧是老的c
>>>
a在执行
子进程内>>>: 666 # 先出现这两行
# 隔了三秒
主在执行
1000
___________________________________
# 注意 不管子进程用不用破界声明关键字,都不影响主进程!前面说过是复制代码到内存中,数据隔离,隔离,隔离(默认情况下)
2.查看进程的ID号
可以用cmd,输入tasklist, 可以看到一排排的任务,有一栏是PID,这个就是进程ID号
在python中如何查看:
- 用current_process模块——current_process().pid
- 或者用os模块——os.getpid() 、os.getppid() 后者能获取主进程的ID号
from multiprocessing import Process, current_process
import time
from multiprocessing import Queue
c = 1000
def a():
print(current_process())
print(current_process().pid)
print('a在执行')
global c
c = 666
print('子进程内>>>:', c)
time.sleep(3)
def b():
print(current_process())
print(current_process().pid)
print('b在执行')
if __name__ == '__main__':
p1 = Process(target=a)
p1.start() # 告诉操作系统创建一个新的进程,并执行
p1.join() # 主进程代码等待子进程代码运行结束再执行
p2 = Process(target=b)
p2.start()
p2.join()
print(current_process())
print(current_process().pid)
print('主在执行')
print('主进程内>>>:', c)
>>>
<Process name='Process-1' parent=7596 started>
14232
a在执行
子进程内>>>: 666
<Process name='Process-2' parent=7596 started>
24180
b在执行
<_MainProcess name='MainProcess' parent=None started>
7596
主在执行
主进程内>>>: 1000
# ID号就显而易见了,parent是代表父进程
3.如何中止进程
代码中:terminate() 终止子进程,当然,也是异步操作,所以如果主程序有延迟启动,可能终止的不及时
cmd中手动:taskkill /F /PID +ID /F是强制模式
- 你也可以pycharm中先拿到进程ID,然后多阻塞一些,立马跑去cmd里去杀掉该ID的进程,也可以
4.判断进程是否还活着方法
is_alive()返回布尔值 同样注意异步情况
if __name__ == '__main__':
obj = A('jason') # 新建一个对象
obj.start() # 创建新进程
obj.terminate()
print(obj.is_alive())
print('主程序执行了')
>>>
True
主程序执行了
_______________________________________
if __name__ == '__main__':
obj = A('jason') # 新建一个对象
obj.start() # 创建新进程
obj.terminate()
time.sleep(0.01) # 加一点点阻塞
print(obj.is_alive())
print('主程序执行了')
>>>
False
主程序执行了
四、消息队列(先了解)
1.含义
临时保存数据的地方,类似公共的仓库,其实我们聊的微信这些程序都运用了高级的消息队列方法,假如我们下线了,别人发消息过来,我们上线后依旧可以收到并查看
2.代码
from multiprocessing import Queue
q = Queue(3) # 指定仓库的容积
q.put(111)
q.put(222)
q.put(333)
print(q.get())
print(q.get())
print(q.get())
>>>
111
222
333
# 先进先出原则,取完就没了,但是再取不报错,会等着新进入数据
——————————————————————————————————————————————
# 几个小方法
print(q.full()) # 判断仓库是否存满了 多进程中不能用
print(q.empty()) # 判断仓库是否是空的 多进程中不能用
print(q.get_nowait()) # 暴躁老哥,不给他就报错
# 实现了进程之间的信息交互
from multiprocessing import Process, Queue
def a(q):
date = '黄焖鸡米饭'
q.put(date)
print(f'我是a,这是我添加的消息队列的数据 {date}')
def b(q):
print('我是b,这是我能看到的消息队列的数据>>>:', q.get())
if __name__ == '__main__':
q = Queue()
p1 = Process(target=a, args=(q,))
p2 = Process(target=b, args=(q,))
p1.start()
p2.start()
print('我是主进程,执行了')
>>>
我是主进程,执行了
我是a,这是我添加的消息队列的数据 黄焖鸡米饭
我是b,这是我能看到的消息队列的数据>>>: 黄焖鸡米饭
五、守护进程
被守护进程会随着守护进程的结束而结束
守护经常的使用
from multiprocessing import Process, Queue
import time
def a(q):
date = '黄焖鸡米饭'
q.put(date)
print(f'我是a,这是我添加的消息队列的数据 {date}')
def b(q):
print('我是b,哈哈我还活着!')
time.sleep(3)
print('我是b进程,这是我能看到的消息队列的数据>>>:', q.get())
if __name__ == '__main__':
q = Queue()
p1 = Process(target=a, args=(q,))
p2 = Process(target=b, args=(q,))
p1.start()
p2.daemon = True # 设置p2子进程为主进程的守护进程
p2.start()
time.sleep(1)
print('我是主进程,执行了')
print('我是被守护进程,接下来我来终结守护进程')
>>>
我是a,这是我添加的消息队列的数据 黄焖鸡米饭
我是b,哈哈我还活着!
我是主进程,执行了
我是被守护进程,接下来我来终结守护进程
# 可以看到,主进程结束后p2进程直接不执行了
六、僵尸进程和孤儿进程
1.僵尸进程
主进程结束后其实不是真正的结束,而是等着所有子进程结束后回收一些信息后再结束
2.孤儿进程
主进程以外嗝屁,子进程没有了父进程,这时候操作系统会派一个类似孤儿院的程序来接管这些子进程。
七、互斥锁
当多个子进程去查询数据库的某个信息时,接收到的信息是请求的那个时间节点的,可能存在后续信息发送了变动,而子进程却没有再次查询,这样就会造成数据错乱
# 先创一个date.json,模拟一下数据库,里面有多少张票
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)
# print('当前余票为:%s' % (data.get('ticket_num')))
# 模拟网络延迟
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()
>>>
用户0在查票 当前余票为:1
用户1在查票 当前余票为:1
用户2在查票 当前余票为:1
用户3在查票 当前余票为:1
用户4在查票 当前余票为:1
用户5在查票 当前余票为:1
用户7在查票 当前余票为:1
用户6在查票 当前余票为:1
用户8在查票 当前余票为:1
用户9在查票 当前余票为:1
用户7买票成功
用户0买票成功
用户1买票成功用户2买票成功
用户4买票成功
用户8买票成功
用户3买票成功
用户9买票成功
用户6买票成功
用户5买票成功
# 可以看到很多BUG,只有一张票,但是所有人都显示买票成功
标签:__,Process,name,python,day34,互斥,print,执行,进程
From: https://www.cnblogs.com/wznn125ml/p/16904713.html