进程

进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。
一. 多道技术
内存中同时存入多道(多个)程序,cpu从一个进程快速切换到另外一个,使每个进程各自运行几十或几百毫秒,
这样,虽然在某一个瞬间,一个cpu只能执行一个任务,但在1秒内,cpu却可以运行多个进程,这就给人产生了并行的错觉,
即伪并行,以此来区分多处理器操作系统的真正硬件并行(多个cpu共享同一个物理内存)
1. 背景: 针对单核,实现并发
2. 空间复用: 如内存中同时有多道程序
3. 时间复用: 复用一个cpu的时间片
遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,
这样才能保证下次切换回来时,能基于上次切走的位置继续运行
二. 进程
进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。
进程的特征:
动态性: 进程是动态产生,动态消亡的。
并发性: 任何进程都可以同其他进程一起并发执行
独立性: 进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;
异步性: 由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
进程与程序的区别:
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
程序可以作为一种软件资料长期存在,而进程是有一定生命期的。
程序是永久的,进程是暂时的。
三. 进程调度
1.先来先服务:
FCFS 算法比较有利于长作业(进程),而不利于短作业(进程)。
由此可知,本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)。
2.短作业优先:
对短作业或短进程优先调度的算法,该算法既可用于作业调度,也可用于进程调度。但其对长作业不利;
不能保证紧迫性作业(进程)被及时处理;作业的长短只是被估算出来的。
3.时间片轮转法:
让每个进程在就绪队列中的等待时间与享受服务的时间成比例
4.多级反馈队列:
设置多个就绪队列,并为各个队列赋予不同的优先级。
第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。
该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。
四. 并发与并行
并发: 是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)
并行: 同时运行,只有具备多个cpu才能实现并行
五. 同步\异步\阻塞\非阻塞
1. 进程状态介绍
就绪: 程序开始运行之后,不是立即开始执行代码,而是进入就绪状态,等待操作系统调度开始运行
运行: 运行状态
阻塞: 由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。
这三个状态不断的转换
2. 同步阻塞形式
效率最低,只能干一件事,期间不许干别的
3. 异步阻塞形式
异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。
4. 同步非阻塞形式
可以在原地干另一件事
5. 异步非阻塞形式
可以去干另一件事,不会在等待消息通知时被阻塞。
六. multiprocess模块
1. 创建进程的两种方法:
1). Process
from multiprocess import Process
定义一个函数
if __name__ == "__main__":
p1 = Process(target=函数名,args=(位置参数,)/kwargs{"与形参相同":value})
p1.start()
2). 继承Process
class MyProcess(Process):
def __init__(self): # 可以自己定义
super().__init__()
def run(self):
pass
if __name__ == "__main__":
p1 = MyProcess(需要传的参数)
p1.start() # 默认执行run方法
p1.join() # 等当前p1进程执行完再向下执行print内容
p1.terminate() # 向操作系统发送可以终止该进程的信号,由操作系统终止该进程
p1.is_alive() # 判断进程是否存活 返回True/False
print(p1.pid) # 查看进程号
print()
2. 开启多进程
用for循环: (所有的子进程异步执行,所有的子进程全部执行完之后,再执行主进程)
if __name__ == '__main__':
p_list= []
for i in range(10):
p = Process(target=func,args=('姑娘%s'%i,'来玩啊!'))
p_list.append(p)
p.start()
for el in p_list:
el.join()
print()
3. 僵尸进程与孤儿进程
1) 僵尸进程(有害)
2) 孤儿进程(无害)
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。
孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
4. 守护进程
p.daemon = True
一定在p.start()之前设置
主进程创建守护进程
      其一:守护进程会在主进程代码执行结束后就终止
      其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
    注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
5. 锁(进程同步)
运行效率低,但保证数据的安全,将部分异步改为同步
抢票:
from multiprocessing import Process,Lock
import time,json,random
def get_ticket(i, ticket_lock):
print("大家都到了,准备")
# time.sleep(random.random())
time.sleep(1)
ticket_lock.acquire()
with open("db",mode="r") as f:
last_ticket_info = json.load(f)
last_ticket = last_ticket_info["count"]
if last_ticket > 0:
last_ticket = last_ticket - 1
last_ticket_info["count"] = last_ticket
with open("db",mode="w") as f:
json.dump(last_ticket_info,f)
print("%s号抢到了" % i)
else:
print("%s号没有抢到票" % i)
ticket_lock.release()
if __name__ == '__main__':
ticket_lock = Lock()
for i in range(10):
p = Process(target=get_ticket, args=(i, ticket_lock,))
p.start()
6. 队列Queue
实现进程间通信(先进先出)
q = Queue() 可以设置队列项数
q.put() 放数据,若队列满了,阻塞,等有空间
q.get() 取数据,若没有数据了,阻塞,等有新得数据进入
q.get(False) 用try试着往出拿数据
q.empty() 为空返回True,不空返回False,但数据不可靠
q.full() 满了返回True,不满返回False,同样数据不可靠
q.qsize() 返回当前队列中的数量,结果不可靠
7. 生产组消费者模型
在并发编程中使用生产者和消费者模式能够解决绝大多数的并发问题
生产者和消费者通过阻塞队列进行通讯(一个缓冲区),进程都到缓冲区去获取或添加数据
即:
一个负责生产数据(生产者)
缓冲区
一个负责处理数据(消费者)
JoinableQueue() :
p = JoinableQueue()
q.task_done() 消费者用此方法发出已经get的信号 get完阻塞
q.join() 生产者调用此方法阻塞,直到队列中的所有项都被拿走
即接收q.task_done()发出的信号
因为q.task_done()结束后会阻塞住,所以将消费者设定为守护进程
并join生产者,这样当生产者结束后执行主进程会将消费者进程也停止
8. Event 事件
e = Event() 默认是False
e.set() 将False改为True
e.wait() 当为True是向下执行,False是会阻塞住
e.is_set() 判断当前e.set()是True/False
模拟红绿灯:
from multiprocessing import Process,Semaphore,Event
import time
def traffic_lights(e):
while 1:
print("当前是红灯")
time.sleep(7)
e.set()
print("绿灯了,可以通行")
time.sleep(6)
e.clear()
def car(i,e):
if not e.is_set():
print("等待绿灯亮")
e.wait()
print("可以走了")
else:
print("直接通过")
if __name__ == '__main__':
e = Event()
lights = Process(target=traffic_lights,args=(e,))
lights.start()
while 1:
time.sleep(2)
for i in range(3):
p1 = Process(target=car,args=(i,e))
p1.start()
9. 管道(进程间的通信) Pipe()
conn1, conn2 = Pipe() 全双工 一端发送数据必须由另一端接收
conn2.send()
conn1.recv() 接收conn2.send()的数据
如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
conn1.close() 关闭连接
posted @ 2018-10-30 21:13  GU99  阅读(145)  评论(0编辑  收藏  举报