yaya

今天也要加油鸭!!!

进程

进程

进程的理论

进程

进程调度

进程三态

同步与异步,阻塞与非阻塞

多道技术(补充):
有4个cpu,运行cpu1的某个程序遇到io阻塞,cpu1会切出去,等到io结束再重新调度,有可能回来的就不是cpu1,是4个cpu中任意的一个
空间上的复用:
内存中同时有多道程序,多道程序的数据之间是不会出现混乱,严格意义上的物理隔离,进程与进程之间是不能交互的,(如暴风雨音中看’小猪佩奇‘,另一个看’喜羊羊‘)
程序:一堆代码
进程:正在运行的程序
	进程是一个实体,每一个进程都有它自己独立的内存空间,是系统进行资源分配和调度的基本单位,:进程是一个具有	一定独立功能的程序关于某个数据集合的一次运行活动。
进程调度:
要想多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随即进行的,而是需要遵循一定的法则,由此就有了进程的调度算法。
1、先来先服务调度法
	先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。FCFS算法比较有利于长作业(进程),而不利于短作业(进程)。由此可知,本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)
2、短作业优先调度算法
短作业(进程)优先调度算法(SJ/PF)是指对短作业或短进程优先调度的算法,该算法既可用于作业调度,也可用于进程调度。但其对长作业不利;不能保证紧迫性作业(进程)被及时处理;作业的长短只是被估算出来的
3、时间片轮转法
4、多级反馈队列(以下有图1)

进程三态:(以下有图2,详解)

同步和异步:针对任务的提交方式
	同步:提交任务之后原地等待任务的返回结果,期间不做任何事!(比如叫朋友去吃饭,要等朋友收拾好了再去吃饭)
	异步:提交任务之后,不等待任务的返回结果,执行运行下一行代码!(叫朋友去吃饭,只是告诉朋友一声就走了,任		务的返回结果是要的,只是通过其他方式获取,而不是立即获取)
		
阻塞与非阻塞:针对程序运行的状态
	阻塞:遇到io操作   >>> 阻塞态
	非阻塞:就绪或者运行态  >>> 就绪态,运行态
 
同步阻塞形式
  效率最低。拿上面的例子来说,就是你专心排队,什么别的事都不做。

异步阻塞形式
  如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知),也就是领了一张小纸条,假如在这段时	  间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;

  异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。

同步非阻塞形式
  实际上是效率低下的。

  想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的。

异步非阻塞形式
  效率更高,

  因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。

  比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面	  通知我一下,那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。

  

很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表现出来,同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞。

时间片(多核cpu和单核cpu都有),时间片是最多占用cpu的时间,假设时间片长度为0.2s,那么就会给每个进程分配0.2s时间运行,如果在优先级最高层 进程运行0.2s过后,进程还没运行完,会将进程往下移(第二层),再运行大于0.2s如果还没运行完,就会再往下移(第三层),依次往下移。(时间片的长度会随着队列优先级的增加而减少)

优先级最高表示占用cpu时间最长,也是cpu最先运行的级别,再依次往下推。在只有一个进程的情况下,也只会运行这一个进程。如果有其他的进程入侵,在低于优先级队列的运行中的进程,会将当前运行进程暂停,cpu就会跑去运行优先级最高的入侵进程,0.2s后就切换回之前运行的进程。

1、程序启动,并不会立即运行,而是先进入就绪状态

2、而当进程在阻塞状态(input,sleep,recv,accept),直到接收值后并不会再继续运行而是会进入阻塞状态(相当于在超时买东西,排队结账,中途又回去拿东西,再来结账时是需要重新排队结账的)

在开发的过程中,尽量在就绪和运行态,避免在阻塞态

创建进程的两种方式

第一种方式
# 程序就是死代码,进程就是正在运行的程序
# 写一串代码,右键运行一个py文件也算创建进程,想在代码中又创建一个子进程

# 注意,在windows系统中,创建进程会将代码以模块的方式从头到尾加载一遍(相当于导入模块,也就是导入写进程的文件)
# import 一个模块,把这个模块中所有的变量名加载一遍放到名称空间里面,给py文件来用


# 比如登录两个qq,相当于两个进程,而这两个进程都有qq的相同所有代码和功能,而这连个进程又是独立的
# 如果进程与进程之间是一个相同的程序,我运行两次能实现相同的功能,就说明拥有的代码是一样的
# 在进程中创建进程,这两个进程的代码都是一样的,在windows系统中,创建进程会将代码以模块的方式从头到尾复制一遍给第二个进程

from multiprocessing import Process  # process是一个类
import time # import 一个模块,把这个模块中所有的变量名加载一遍放到名称空间里面,给py文件来用

def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)

# 注意,在windows系统中,创建进程会将代码以模块的方式从头到尾加载一遍
# 一定要写在if __name__ == '__main__':代码块里面
# 强调:函数名一旦加括号,执行优先级最高,立刻执行(所以task不能加括号)
# 强调:凡是在一个容器内,也就是存多个值的数据类型,当只有一个元素的时候,是要加一个逗号
if __name__ == '__main__':
    p1 = Process(target=task,args=('egon',))  # 这一句话只是实例化了一个Process对象 ,task('egon')
    p1.start()  # 告诉操作系统创建一个进程,操作系统神什么时候创建就不知道了,相当于异步,只是告诉他了,
    # 后接着运行自己的代码
    print('主') # 这个是主进程的代码



'''
from multiprocessing import Process  # process是一个类
import time # import 一个模块,把这个模块中所有的变量名加载一遍放到名称空间里面,给py文件来用

def task(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)

# 创建进程

if __name__ == '__main__':
        pass



如果创建进程的代码不写在if __name__ == '__main__':下面的话,会产生一个循环导入,因为创建一个进程就是
windows会将以上代码以模块的方式从头到尾加载一遍,执行到创建进程的代码时,发现又要导入模块创建进程,
又走了一遍以上代码又到创建进程这代码又来导

创建进程是以模块的方式来加载一遍,并不是说要真的创建一个文件写创建进程的代码,而是模仿导入模块的操作方式进行的一个操纵
'''
第二种方式
from multiprocessing import Process
import time

class MyProcess(Process):

    def __init__(self, name):
        super().__init__()  
        self.name = name  #必须要将self.name=name 写在supper()下,
        # 因为如果不是这样的话,self.name最终会是父类中的默认的值
        
    # 必须写run方法
    def run(self):
        print('%s is running'%self.name)
        time.sleep(1)
        print('%s is end'%self.name)

if __name__ == '__main__':
    obj = MyProcess('egon')
    obj.start()
    print('主')

进程对象的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__':
    start_time = time.time()
    p_list = []
    for i in range(3):
        p = Process(target=task,args=('子进程%s'%i,i))  每次造出的对象都是不一样的,名字虽然相同
        p.start()
        p_list.append(p)
    for i in p_list:
        i.join()
    # p1 = Process(target=task,args=('egon',1))
    # p2 = Process(target=task,args=('jason',2))
    # p3 = Process(target=task,args=('kevin',3))
    # start_time = time.time()
    # p1.start()  # 千万要知道,这句话只是告诉操作系统需要进程
    # p2.start()
    # p3.start()
    # # p1.join()  # 主进程等待子进程结束 才继续运行
    # p3.join()
    # p1.join()
    # p2.join()
    print('主',time.time()-start_time)

# join的作用仅仅只是让主进程等待子进程的结束,不会影响子进程的运行

进程间数据隔离

# 记住 进程与进程之间数据是隔离的!!! 
from multiprocessing import Process


x = 100

def task():
    global x #改变全局变量x
    x = 1


if __name__ == '__main__':
    p = Process(target=task)
    p.start()  
    p.join()
    print('主',x)  # x=100  ,这个是主进程的x=100

互斥锁

牺牲了效率但是保证了数据的安全


​ 锁一定要在主进程中创建,给子进程去用
​ 解决多个进程操作同一份数据,造成数据不安全的情况
​ 加锁会将并发变成串行
​ 锁通常用在对数据操作的部分,并不是对进程全程加锁

mutex.acquire() # 抢锁 一把锁不能同时被多个人使用,没有抢到的人,就一直等待锁释放
buy(i)
mutex.release() # 释放锁

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

def search(i):
    with open('info','r',encoding='utf-8') as f:
        data = json.load(f)
    print('用户查询余票数:%s'%data.get('ticket'))


def buy(i):
    # 买票之前还得先查有没有票!
    with open('info','r',encoding='utf-8') as f:
        data = json.load(f)
    time.sleep(random.randint(1,3))  # 模拟网络延迟
    if data.get('ticket') >0:
        data['ticket'] -= 1  # 买票
        with open('info','w',encoding='utf-8') as f:
            json.dump(data,f)
        print('用户%s抢票成功'%i)
    else:
        print("用户%s查询余票为0"%i)


def run(i,mutex):
    search(i)
    mutex.acquire()  # 抢锁   一把锁不能同时被多个人使用,没有抢到的人,就一直等待锁释放
    buy(i)
    mutex.release()  # 释放锁


if __name__ == '__main__':
    mutex = Lock()
    for i in range(10):
        p = Process(target=run,args=(i,mutex))
        p.start()

守护进程(了解)

from multiprocessing import Process
import time

def task(name):
    print('%s 正活着'%name)
    time.sleep(3)
    print('%s 正常死亡'%name)


if __name__ == '__main__':
    p = Process(target=task,args=('egon总管',))
    p.daemon = True  # 必须在p.start开启进程命令之前声明
    p.start()
    print('皇帝jason正在死亡')

进程对象其他相关方法(了解)

from multiprocessing import Process,current_process
import time
import os


def task():
    print('%s is running'%os.getpid())
    time.sleep(3)
    print('%s is over'%os.getppid())


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()
    # p1.terminate()  # 杀死子进程
    # time.sleep(0.1)
    # print(p1.is_alive())  # 判断子进程是否存活
    print('主')

僵尸进程与孤儿进程(了解)

两种情况下会回收子进程的pid等信息
1.父进程正常结束
2.join方法

	孤儿进程:父进程意外意外死亡
	
	linux下:
		init孤儿福利院;用来挥手孤儿进程所占用的资源
	ps aux |grep 'Z'
posted @ 2019-05-06 21:43  Tiffany'.'  阅读(266)  评论(0编辑  收藏  举报