进程
创建进程的方式
"""
1.双击桌面程序图标
2.代码创建进程(需要掌握)
需要掌握两种即可
"""
from multiprocessing import Process
import time
def task(name):
print(f'{name}开始运行')
time.sleep(1) # 运行进程
print(f'{name}结束运行')
if __name__ == '__main__': # 启动脚本
p = Process(target=task, args=('game',)) # 创建一个进程对象
p.start() # 子进程开始执行
print('主进程结束')
# 主进程结束
# game开始运行
# game结束运行
task('game') # 直接函数调用是同步操作
print('主进程结束')
# game开始运行
# game结束运行
# 主进程结束
'''
在不同的操作系统创建进程的代码中,底层原理是有区别的
在windows中,创建进程类似于导入模块,导入模块是又会从头开始在执行一遍代码
这样就会在执行一次创建进程,如此循环,所以要加入启动脚本:
if __name__ == '__main__':
而在mac、linux中,创建进程类似于直接拷贝
不需要启动脚本,但是为了兼容性,也可以用
'''
class MyProcess(Process):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f'{self.name}开始运行')
time.sleep(1) # 运行进程
print(f'{self.name}结束运行')
if __name__ == '__main__':
obj = MyProcess('game')
obj.start() # 子进程开始执行
print('主进程结束')
# 主进程结束
# game开始运行
# game结束运行
join方法
'''
join:主进程等待子进程运行结束之后再运行
无法把握子进程执行的时间,所以在主进程代码中添加time.sleep()不行
'''
from multiprocessing import Process
import time
def task(name, n):
print(f'{name}开始运行')
time.sleep(n) # 运行进程
print(f'{name}结束运行')
if __name__ == '__main__': # 启动脚本
p = Process(target=task, args=('game', 5)) # 创建一个进程对象
start_time = time.time() # 开始时间
p.start() # 子进程开始执行
p.join()
end_time = time.time() - start_time # 结束时间
print(f'总耗时:{end_time}')
print('主进程结束')
# game开始运行
# game结束运行
# 总耗时:5.092717170715332
# 主进程结束
进程间数据默认隔离
"进程之间的数据是相互隔离的,如果想要交互需要借助于'管道'或者'队列'"
from multiprocessing import Process
money = 666
def task():
global money
money = 233
print(f'子进程的money是{money}')
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join()
print(f'父进程的money是{money}')
# 子进程的money是233
# 父进程的money是666
进程间通信(IPC机制)
1.对列:先进先出
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.empty()) # 判断对列是否已空
# False
print(q.get())
# 22
print(q.get())
# 33
print(q.empty())
# True
print(q.get()) # 超出数据获取极限时程序会一直处于阻塞态,直到队列中有数据被添加
print(q.get_nowait())
# 11
print(q.get_nowait())
# 22
print(q.get_nowait())
# 33
print(q.get_nowait()) # 超出数据获取极限时程序会报错
'''
q.full() # 刚判断对列为满,其他进程又取出数据
q.empty() # 刚判断对列为空,其他进程又添加数据
q.get_nowait() # 刚超出数据获取极限报错,其他进程又添加数据
上述方法在在多进程下可能会失效
'''
2.IPC机制
from multiprocessing import Queue, Process
def procedure(q):
q.put('子进程procedure往对列中添加数据') # 将数据添加到对列
def consumer(q):
print('子进程consumer从对列中获取数据', q.get()) # 打印字符串与对列中获取的数据
if __name__ == '__main__':
q = Queue() # 在主进程中产生q对象,让所有的进程使用的都是相同的q
p1 = Process(target=procedure, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
print('主进程结束')
# 主进程结束
# 子进程consumer从对列中获取数据 子进程procedure往对列中添加数据
生产者消费者模型
生产者:产生数据,在将数据放到对列里
缓冲区(消息队列/数据库):存放数据的地方
消费者:从队列里面取数据,处理数据
进程相关方法
1.查看进程号
from multiprocessing import current_process
import os
print(current_process().pid) # 查看进程号
# 9668
print(os.getpid()) # 查看进程号
# 9668
print(os.getppid()) # 查看主进程号
# 13012
2.销毁子进程
p1.terminate()
3.判断进程是否存活
p1.is_alive()
守护进程
'守护进程结束时,守护进程立刻结束'
from multiprocessing import Process
def task(name):
print(f'{name}死了')
if __name__ == '__main__':
p = Process(target=task, args=('伏地魔',))
p.daemon = True # 将子进程设置为守护进程(必须在start之前执行)
p.start()
print('魂器没了')
# 魂器没了 主进程代码结束,子进程立刻结束
僵尸进程与孤儿进程
1.僵尸进程
僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
2.孤儿进程
孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作
互斥锁
'''
当多个进程同时操作一个数据的时候,可能会导致数据混乱,我们可以给他加锁,让所有进程排队执行,虽然会让程序变得更慢
'''
from multiprocessing import Process, Lock
import time
import json
import random
def search(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
date = json.load(f)
print('%s在查票 当前余票为:%s' % (name, date.get('ticket_num')))
def buy(name):
with open(r'data.txt', 'r', encoding='utf8') as f:
date = json.load(f)
time.sleep(random.randint(1, 3))
if date.get('ticket_num') > 0:
date['ticket_num'] -= 1
with open(r'src.py', 'w', encoding='utf8') as f:
json.dump(date, f)
print(f'{name}买票成功')
else:
print(f'{name}没有抢到票')
def run(name, mutex):
search(name)
mutex.acquire() # 抢锁
buy(name)
mutex.release() # 释放锁
if __name__ == '__main__':
mutex = Lock() #在主程序生成一个锁,那个子进程先抢到先执行
for i in range(5):
p = Process(target=run, args=(f'{i}', mutex))
p.start()
data = {"ticket_num": 1}
with open(r'data.txt', 'w', encoding='utf8') as f:
json.dump(data, f)
# 2在查票 当前余票为:0
# 4在查票 当前余票为:0
# 3在查票 当前余票为:0
# 1在查票 当前余票为:0
# 0在查票 当前余票为:0
# 2没有抢到票
# 4没有抢到票
# 3没有抢到票
# 1没有抢到票
# 0没有抢到票
作业
# 尝试将TCP服务端制作成并发效果
# 客户端服务端全部设置成自动发消息自动回消息
# eg: 客户端发hello 服务端直接转大写回HELLO
服务端
from multiprocessing import Process
import socket
def talk(sock):
while True:
try:
data = sock.recv(1024)
if len(data) == 0:
break
print(data.decode('utf-8'))
sock.send(b'HELLO')
except ConnectionError as e:
print(e)
break
sock.close()
if __name__ == '__main__':
while True:
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
sock, address = server.accept()
p = Process(target=talk, args=(sock,))
p.start()
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))
while True:
client.send(b'hello')
data = client.recv(1024)
print(data.decode('utf-8'))