进程的其他方法,守护进程,互斥锁,队列,进程间通信

一.上次课程回顾

操作系统发展史

https://www.cnblogs.com/xiaoyuanqujing/articles/11715635.html

多道技术(了解)

https://www.cnblogs.com/xiaoyuanqujing/articles/11715646.html

同步异步 阻塞非阻塞

python中
-flash,Django3.0以前,都是同步框架
-tornado,sanic,fastAPI 异步框架

同步:

一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,
依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,
失败都失败,两个任务的状态可以保持一致。

异步:

不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,
依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终
是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

创建进程的2种方式(重点)

第一种

from multiprocessing import Process
import time

def task(n):
	print('我是子进程')
	time.sleep(n)
	print('子进程结束')

if __name__ == '__main__':
	# t=Process(target=task,args=(1,))
	t = Process(target=task,kwargs={'n':1})
	t.start()  # 通知操作系统,开启进程,执行task函数
	print('主')

# 输出结果
'''
主
我是子进程
子进程结束
'''

第二种

from multiprocessing import Process
import time

class Task(Process):
	def __init__(self,n):
		super().__init__()
		self.n = n

	def run(self):
		print('我是子进程')
		time.sleep(self.n)
		print('子进程结束')

if __name__ == '__main__':
	t=Task(1)
	# t.run(1)  # error 不是调用t.run(),而是调用t.start()
	t.start()
	print('主')
	
# 输出结果
'''
主
我是子进程
子进程结束
'''

join方法

join的使用:等待子进程执行完成

from multiprocessing import Process
import time

def task(n):
	print('我是子进程')
	time.sleep(n)
	print('子进程结束')

if __name__ == '__main__':
	ctime=time.time()
	t=Process(target=task,kwargs={'n':1})
	t2 = Process(target=task, kwargs={'n': 2})
	t.start()
	t2.start()
	t.join()  # 等待t子进程执行完成
	t2.join()  # 等待t2子进程执行完成
	print('主')
	ctime2=time.time()
	print(ctime2-ctime)

# 输出结果
'''
我是子进程
我是子进程
子进程结束
子进程结束
主
2.084543466567993
'''

进程间数据相互隔离

进程隔离的设计目的:

为保护操作系统中进程互不干扰而设计的(为了避免进程A写入进程B的情况发生)
from multiprocessing import Process
import time

age=18
def task(n):
	global age  # 局部修改全局
	age=99
	print('我是子进程')
	time.sleep(n)
	print('子进程结束')
	print(age)

if __name__ == '__main__':
	t=Process(target=task,args=(1,))
	t.start()
	t.join()  # 等待t子进程执行完成
	print('主')
	print(age)  # 数据没有变,主进程中打印age和子进程的age没有关系,数据是隔离的
	
# 输出结果
'''
我是子进程
子进程结束
99
主
18
'''

二.今日内容

进程调度

用户进程数一般都多于处理机数、这将导致它们互相争夺处理机。另外,系统进程也同样需要使用处理机。这就要求进程调度程序按一定的策略,动态地把处理机分配给处于就绪队列中的某一个进程,以使之执行。

进程调度算法:
1.先来先服务
2.短作业优先
3.时间片轮转
4.多级反馈队列

from multiprocessing import Process,current_process
import time
import os

# 每个进程都会有自己的id号pid

def task():
	print('子进程')

	# 当前进程的id号
	print(current_process().pid)
	# print(os.getpid())  # 跟上面打印出来一模一样的
	# 取出该进程父id号
	print('-------',os.getppid())
	# current_process()  # 当前进程对象

	print(current_process().is_alive())
	time.sleep(2)
	print('子进程结束')

if __name__ == '__main__':
	t=Process(target=task)
	t.start()
	# t1=Process(target=task)
	# t1.start()
	# t.is_alive()  # t这个进程是否存活
	# print('主进程打印的结果',t.is_alive())
	print(t.pid)
	time.sleep(0.5)
	# t.terminate()  # 把t进程关闭
	# time.sleep(0.1)
	print('主进程打印的结果',t.is_alive())

# 输出结果
'''
15724
子进程
15724
------- 16636
True
主进程打印的结果 True
子进程结束
'''

僵尸进程与孤儿进程

1.僵尸进程:进程结束了,资源还没来得及回收
2.孤儿进程:主进程挂了,子进程还没结束,它就会被专门的进程接管

进程对象及其他方法

1.windows: tasklist | findstr 进程id号
2.mac,Linux: ps aux | grep 进程id号
3.进程对象:t=Process(target=task, )或者是在进程内部:current_process()
# 4 t.pid或者current_process().pid   获取进程id号
5 os.getpid() 同上,获取进程id号
6 os.getppid() 获取父进程id号,子进程中获取父进程id,等于父进程的id号
7 t.is_alive()或者current_process().is_alive()  查看进程是否存活
8 t.terminate() 关闭进程,在主进程关闭

守护进程

守护进程:会随着主进程的结束而结束

主进程创建守护进程

1.守护进程会在主进程代码执行结束后就终止
2.守护进程内无法再开启子进程,否则会抛出异常

注意:进程之间是相互独立的,主进程代码运行结束,守护进程随即终止
守护进程一定要在子进程开始(子进程.start())执行之前设置 子进程.daemon=True

from multiprocessing import Process,current_process
import os
import time

def task():
	# print(os.getpid())
	print('子进程')
	time.sleep(2)
	print('子进程结束')

if __name__ == '__main__':
	t=Process(target=task)
	# 守护进程:主进程一旦结束,子进程也结束
	t.daemon=True  # 一定要加在启动之前
	t.start()

	time.sleep(1)
	print('主进程结束')
	print(os.getpid())
	time.sleep(1)

# 输出结果
# 当主进程先于子进程结束之后,
# 它的守护进程t子进程也会跟着结束掉,所以不会打印子进程结束
'''
子进程
主进程结束
16444
'''

问题1:主进程的父进程是谁?

pycharm

问题2:主进程开了很多子进程,每个都需要设置守护吗?

看需求,
想要某个进程是守护:t.daemon=True

互斥锁

进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,

而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理

同时只有一个人能拿到,必须释放,其他人才能再次获取到

多进程模拟抢票实例

from multiprocessing import Process, Lock
import json
import time
import random


def search():
    # 查票的函数
    # 打开文件,读出ticket_count
    with open('ticket', 'r', encoding='utf-8') as f:
        dic = json.load(f)
        print('余票还有:', dic.get('ticket_count'))


def buy():
    with open('ticket', 'r', encoding='utf-8') as f:
        dic = json.load(f)

    time.sleep(random.randint(1, 3))  # 模拟一下网络延迟
    if dic.get('ticket_count') > 0:
        # 能够买票
        dic['ticket_count'] -= 1
        # 保存到文件中去
        with open('ticket', 'w', encoding='utf-8') as f:
            json.dump(dic, f)
            print('买票成功')
    else:
        # 买票失败
        print('买票失败')


# 写一个函数,先查票,再买票

def task(mutex):
    search()
    # 买票过程要加锁
    # 买前加锁
    # mutex.acquire()
    # buy()  # 10个进程变成了串行执行
    # # 买后释放锁
    # mutex.release()
    with mutex:
        buy()


if __name__ == '__main__':
    # 锁的创建,在哪?主进程创建锁
    mutex = Lock()  # 创建一把锁
    # 模拟十个人买票(开10个进程)
    for i in range(10):
        t = Process(target=task, args=(mutex,))
        t.start()

总结:加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,虽然速度慢,但是保证了数据的安全
此种方法缺点:

效率低(共享数据基于文件,而文件是硬盘上的数据)
需要自己加锁处理

魔法方法之__enter__和__exit__

面向对象高级:魔法方法(__开头的),enter__和__exit,上下文管理

自己写一个类,实现类似于打开文件with open的功能

with MyClass('文件名','方式','编码')as f:
	f.read()
# 在这里写代码,f就关闭了
class MyClass():
    def __init__(self,file_name,mode,encoding):
        self.file_name=file_name
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        print('只要有with,就会执行我')
        self.file=open(self.file_name,self.mode,encoding=self.encoding)

        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 只要顶格写代码,就会执行我
        print('只要顶格写代码,就会执行我')
        self.file.close()


with MyClass('ticket','r','utf-8') as f:
    print(f.read())
    print('xxss')
    print("sdfadasf")


# a=MyClass('ticket','r','utf-8')

# 输出
'''
只要有with,就会执行我
{"ticket_count": 0}
xxss
sdfadasf
只要顶格写代码,就会执行我
'''

队列介绍

队列指创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递

总结一下就是队列支持多个人从队列的一段放入数据,同样支持多个人从队列的另一端取数据

基本用法

Queue([maxsize]) # 创建共享的进程队列 队列底层使用管道和锁定实现。
# 参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
from multiprocessing import Queue

# 实例化得到一个对象

q=Queue(5)  # 默认很大,可以放很多,写了个5,只能放5个

# 往管道中放值
q.put(1)
q.put('lqz')
q.put(18)
q.put(19)
# q.put(20)
# q.put(21)
# q.put_nowait(100)

# 从管道中取值
# print(q.get())
# print(q.get())
# print(q.get())
# print(q.get(timeout=100))  # 等0.1s还没有值,就结束
# print(q.get_nowait())        # 不等了,有就是有,没有就没有

print(q.empty())  # 看一下队列是不是空的
print(q.full())   # 看一下队列是不是满的

总结:

q = Queue(队列大小)
# 放值 
q.put(asdf) 
q.put_nowait(asdf)  # 队列满了,放不进去就不放了,报错

# 取值 
q.get()  # 从队列头部取出一个值 
q.get_nowait()  # 从队列头部取值,没有就报错

# 队列是否为空,是否满 
print(q.empty())  # 看一下队列是不是空的 
print(q.full())  # 看一下队列是不是满的

IPC机制(进程间通信)

我们知道进程之间数据是相互隔离的,要想实现进程间的通信,需要借助一些技术,比如multiprocessing模块中的:队列和管道,这两种方式都是可以实现进程间数据传输的,由于队列是管道+锁的方式实现,所以我们着重研究队列即可

from multiprocessing import Process, current_process, Queue
import time
import os


def task1(q):
    print('我是task1进程,我的id号是:%s'%os.getpid())
    q.put('lqz is handsome')


def task2(q):

    # res=q.get()
    # print('我是task2进程,我的id号是:%s'%os.getpid(),res)
    print('我是task2进程,我的id号是:%s'%os.getpid())


if __name__ == '__main__':
    q = Queue(5)

    t1 = Process(target=task1, args=(q,))
    t1.start()
    t2 = Process(target=task2, args=(q,))
    t2.start()

    print(q.get())

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值