进程一
一、什么是进程
进程(process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。我们自己在python文件中写了一些代码,这叫做程序,运行这个python文件的时候,这叫做进程。
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
2,进程的特征
二、并发与并行,同步与异步
并发:伪并行,就是几个进程共用一个cpu,几个进程之间是时间轮换的,而这个轮换时间很短,肉眼是看不到的,所以我们肉眼看到的是几个进程同时在进行,其他中间有停止的,只是停止时间短,这就叫并发
并行:指一个进程就用一个cpu,每个进程是没有停止的,就是一起运行的
同步:一个任务执行完后,另一个任务才能开始执行,就是有一个任务需要等待,这样两个任务之间的关系是同根生的,非常紧密的
异步:两个任务之间没有关联,你执行你的,我执行我的,互相不用等待
三、进程的创建方法
1,方法一
from multiprocessing import process #引入进程模块, import os,time def fun1(): time.sleep(2) print('jjjj') def fun2(): time.sleep(3) print('ddada') print(os.getpid()) #获得子进程的进程id print(os.getppid()) #获得子进程的父进程,即主进程的id if __name__=="__main__": #记住,必须用 if main start_time=time.time() #开始时间 p=process(target=fun2) #创建一个子进程 p.start() #开始子进程 fun1() print(os.getpid()) #获得主进程的进程id end_time=time.time() #结束时间 print(end_time-start_time) #打印总共用时
整段程序走完用时2秒多一点,但是如果我们按照以前的写法,先执行fun1(),再执行fun2(),至少得花5秒
2,方法二
from multiprocessing import process #同样引入模块 import time class mypro(process): #创建一个类,继承process def __init__(self,name): super().__init__() self.name=name def run(self): #这是必须的方法,然后要执行的程序写在方法里面就行 time.sleep(3) print(self.name) if __name__ == '__main__': #记住,这里必须写 if main p1=mypro('huangchun') #用自己定义的类创建一个子进程 p1.start() #子进程开始 p1.join() #这是join方法,这样写就是join后面的主进程程序必须等子进程执行完后在执行,两种方法都可以用 print('我是主进程,哈哈哈哈哈哈。。。。。')
四、守护进程
把一个子进程设为守护进程之后,每当主进程执行完毕后,不管你子进程有没有执行完毕,都要跟着结束
from multiprocessing import process import time def fun(): time.sleep(5) print('我是子进程') if __name__ == '__main__': p=process(target=fun) p.daemon = true #这就是把子进程设为了守护进程,但是切记,这句代码必须写在start()前面 p.start() print('我主进程,哈哈哈哈哈哈')
没有设置守护进程之前,代码要执行5秒才结束,等到子进程执行完才结束,但是设置为守护进程之后,代码瞬间执行完毕,只打印了主进程的内容,而子进程的print直接死了
五、同步锁(互斥锁)
对一段程序设置同步锁之后,不管你对这段程序开了几个进程,但只有一个进程能执行代码,而其他的进程需要等待前面的进程结束之后才能执行这段代码,就相当于把开的几个进程搞成同步了,互相约束着
这是模拟抢火车票的情景,在还没付钱确认之前,大家都看见了有一张票,但是只有一个人能抢到票
from multiprocessing import process,lock #引入lockmok import time def fun1(i): #这是查看余票的方法 with open('piao.txt',mode='r',encoding='utf-8')as f1: num=f1.read() print('%s客户看到票还有%s张'%(i,num)) def fun(i,lo): #这是买票的方法 time.sleep(1) lo.acquire() #这是同步锁的开始, with open('piao.txt',mode='r',encoding='utf-8')as f1: num=f1.read() if num=='1': print('%s买到票了'%i) num=int(num)-1 elif num=='0': print('%s没票了'%i) with open('piao.txt', mode='w', encoding='utf-8')as f1: f1.write(str(num)) lo.release() 这是同步锁的结束 if __name__ == '__main__': lo=lock() #这是创建一把锁 for i in range(10): #这是循环创建10个客户 fun1(i) #这是客户查看余票 p=process(target=fun,args=(i,lo)) #创建子进程去抢票,然后把锁传给进程 p.start()
同步锁把抢票的程序锁起来了,所以,虽然有10个客户都在抢票,但实际上只有一人能进入抢票程序,其他的人要等待第一个进抢票程序的人执行完后才能又进一个人,反正每次就只允许一个人在使用抢票程序,其他的人继续排队
六、信号量
信号量就是对一段程序设定允许最多几个人使用,相当于一个饭店,只有四个位置,最多允许四个人吃饭,后面人要等待前面的人吃完才能进入饭店吃,前面走一个,就可以进一个
from multiprocessing import process,semaphore #引入semaphore模块 import time def fun(s,i): s.acquire() print('%s客户在吃'%i) time.sleep(1) print('%s客户吃完了'%i) time.sleep(1) s.release() if __name__ == '__main__': s=semaphore(4) #创建信号量对象 for i in range(10): #循环创建10个人去饭店吃饭 p=process(target=fun,args=(s,i)) #创建饭店子进程,然后把信号量传给子进程 p.start()
七、事件
在创建事件之后默认值为false,我们可以把创建的事件对象传给子进程,然后在子进程中事件对象.set(),使得指变为ture。然后我们在主进程中加上事件对象.wait(),在此处,只有当值为ture时不会阻塞,值为false就会阻塞,
从而我们可以用此方法来影响主进程的执行。
from multiprocessing import process,event #引入event模块 import time def func(e): print('子进程开始执行') time.sleep(3) print('子进程结束') e.set() #设置事件值为ture,我们还可以通过e.clear()把值改为false if __name__ == '__main__': e=event() #创建事件对象e,此时默认值为false p=process(target=func,args=(e,)) #创建子进程,把e传给子进程 p.start() print('等待子进程结束后拿到值') e.wait() #当值为false会阻塞,当值为ture是,不会阻塞,因为我们在子进程中最好把值改为ture,所以wait()以后的程序要等到子进程执行完才能执行 print('拿到值,执行主进程') time.sleep(1) print('主进程执行完毕')
八、队列,消费者生产者模型,joinablequeue
1,队列
队列就相当于一个容器,里面可以放数据,特点是先放进去先拿出来,即先进先出,
from multiprocessing import queue #引入queue模块 q=queue(2) #创建一个队列对象,并给他设置容器大小,即能放几个数据 q.put(1) #put()方法是往容器里放数据 q.put(2) q.put(3) #这是往容器里放第三个数据,由于只能放两个,所以此处会阻塞,不会报错,相当于死循环 q.put_nowait(4) #put_nowait()这也是从容器里放数据的方法,但如果容器满了,不会阻塞,会直接报错 q.get() #get()方法是从容器里拿数据 q.get() q.get() #这是从容器里拿第三个数据,但是容器的两个数据拿完了,没有数据了,此时也会阻塞,不会报错,死循环 q.get(false) #这也是从容器里拿数据的方法,当没数据时不会阻塞,直接报错 q.get_nowait() #这也是从容器里拿数据的方法,当没数据时不会阻塞,直接报错 q.full() #这是查看容器是否满了,如果满了,返回ture,否则返回false q.empty() #这是查看容器是否为空,如果为空,返回ture,否则返回false
2,消费者生产者模型
就是消费者和生产者之间不是直接联系的,而是通过中间的一个队列来进行联系的,生产者把生产的东西放进进队列里,消费者从队列里拿东西,其实消费者和生产者之间没有实质的联系
from multiprocessing import process,queue import time def sheng(q): #生产者 for i in range(10): time.sleep(1) q.put(i) print('包子%i生产完毕'%i) q.put('over') def xiao(q): #消费者 while 1: time.sleep(1.5) ss=q.get() if ss=='over': break print('包子%s被吃了'%ss) if __name__ == '__main__': q=queue(10) #创建队列对象q p=process(target=sheng,args=(q,)) #创建生产者子进程,把q传给他 p1=process(target=xiao,args=(q,)) #创建消费者子进程,也把q传给他 p.start() p1.start()
有个问题就是,生产者要把生产结束的消息放进队列里,让消费者过去结束消息,消费者才知道队列里没有数据了,这样消费者才能停止,如果生产者不放结束消息,当生产者结束时,即不往队列放数据,而消费者不知道生产者已经结束,
还一直往队列拿数据,当队列没数据时,消费者一边就会阻塞。解决阻塞,有几个消费者,生产者就应该放几个结束消息,让每个消费者都知道,这样每个消费者才能结束。但是生产者又不知道有几个消费者,所以不知奥应该放几个结束数据,
这样就无法解决对消费者现象。此时,我们就就可以引入joinablequeue
3,joinablequeue
他其实就是一种队列,但她又比队列要多两种方法,task_done()和join()方法,正是有这两种方法就可以解决上面的问题
from multiprocessing import process,joinablequeue #引入joinablequeue模块 import time def sheng(q): #生产者 for i in range(10): time.sleep(1) q.put(i) print('包子%i生产完毕'%i) q.join() #当q收到的task_done数量等于放进q的数据数量时,生产者就结束了 def xiao(q,i): #消费者 while 1: time.sleep(1.5) ss=q.get() if ss=='over': break print('%s客户吃包子%s'%(i,ss)) q.task_done() #消费者每从q里取一个值,就向q返回一个task_done消息 if __name__ == '__main__': q=joinablequeue() #创建joinablequeue对象q p=process(target=sheng,args=(q,)) #创建生产者子进程,把q传给他 p.start() for i in range(3): #循环创建消费者子进程,把q传给他 p1=process(target=xiao,args=(q,i)) p1.daemon=true #把创建的每个消费者子进程设为守护进程 p1.start() p.join() #主进程要等到生产者子进程结束后才结束
整个个过程就是:生产者生产了10个包子,3个消费者吃包子,没吃一个包子往q里发一个task_done,当q拥有10个task_done时,意味着10个包子吃完了,此时,生产者就结束了,接着主进程也也结束了,然后守护进程跟着结束了,即所有的消费者子进程结束,解决上面所遇到的问题
推荐阅读
-
python进程类subprocess的一些操作方法例子
-
php守护进程 加linux命令nohup实现任务每秒执行一次
-
C#程序提示“正由另一进程使用,因此该进程无法访问该文件”的解决办法
-
影响中国历史进程的八位女性排第一的当之无愧
-
Intmonp.exe是一种什么样的进程 是病毒吗 Intmonp进程注解
-
download.exe是一个安全的进程吗 download进程可以结束吗
-
元祐更化改变了中原王朝的历史进程 对北宋王朝来说,是一次严重的打击
-
探讨:Oracle数据库查看一个进程是如何执行相关的实际SQL语句
-
第一次作业:关于Linux 2.6.20进程模型和O(1)调度器算法的分析
-
操作系统与程序运行以及进程简介 多线程上篇(一)