进程
什么是进程?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
进程调度
要想多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随即进行的,而是需要遵循一定的法则,由此就有了进程的调度算法。
了解几个相关的算法
- 先来先服务算法
- 短时间优先调度算法
- 时间片轮转法
- 多级反馈队列
http://www.cnblogs.com/Eva-J/articles/8253549.html#_label11 ——详见女神的博客
进程的并行与并发
并行 : 并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的CPU )
并发 : 并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率。
区别:
并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session。
同步,异步、阻塞与非阻塞
这张图很关键
在了解其他概念之前,我们首先要了解进程的几个状态。在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞。
(1)就绪(Ready)状态 当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
(2)执行/运行(Running)状态 当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
(3)阻塞(Blocked)状态 正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
同步与异步
所谓同步:就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列
。要么成功都成功,失败都失败,两个任务的状态可以保持一致。
例如购物车与结算,必须现将商品添加至购物车才能结算!
所谓异步:是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了
。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列
。
比如我去银行办理业务,可能会有两种方式:
第一种 :选择排队等候;
第二种 :选择取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了;
第一种:前者(排队等候)就是同步等待消息通知,也就是我要一直在等待银行办理业务情况;
第二种:后者(等待别人通知)就是异步等待消息通知。在异步消息处理中,等待消息通知者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码,喊号)找到等待该事件的人。
阻塞与非阻塞
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的
进程的创建与结束
进程的创建
multiprocess.process模块介绍
1 Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 2 3 强调: 4 1. 需要使用关键字的方式来指定参数 5 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号 6 7 参数介绍: 8 1 group参数未使用,值始终为None 9 2 target表示调用对象,即子进程要执行的任务 10 3 args表示调用对象的位置参数元组,args=(1,2,'egon',) 11 4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} 12 5 name为子进程的名称
实例代码:
1 # 在python进程中开启一个子进程 2 import time 3 from multiprocessing import Process 4 5 6 def f(name): 7 print("hello", name) 8 print("我是子进程") 9 10 11 if __name__ == '__main__': 12 p = Process(target=f, args=("pontoon", )) 13 p.start() 14 time.sleep(1) 15 print("执行主进程的内容了") 16 17 >>>hello pontoon 18 我是子进程 19 执行主进程的内容了
1 import os 2 import time 3 from multiprocessing import Process 4 5 6 def func(): 7 print(54321) 8 time.sleep(1) 9 print("子进程:", os.getpid()) 10 print("子进程的父进程:", os.getppid()) 11 print(12345) 12 13 14 if __name__ == '__main__': 15 p = Process(target=func) 16 p.start() 17 print("*" * 20) 18 print("父进程:", os.getpid()) # 查看当前进程的进程号 19 print("父进程的父进程", os.getppid()) 20 21 22 >>>******************** 23 父进程: 2972 24 父进程的父进程 14060 25 54321 26 子进程: 13992 27 子进程的父进程: 2972 28 12345
多进程中的join()方法
进程中join()方法的用处,感知一个子进程的结束,将异步的程序变成同步的程序,在join()方法调用之前,子进程与主进程都是异步的,但是当调用了join()方法之后,那么下面的代码就变成了同步!
1 import os 2 import time 3 from multiprocessing import Process 4 5 6 def func(): 7 print(54321) 8 time.sleep(1) 9 print("子进程:", os.getpid()) 10 print("子进程的父进程:", os.getppid()) 11 print(12345) 12 13 14 if __name__ == '__main__': 15 p = Process(target=func) 16 p.start() 17 p.join() # 将异步的程序变成同步的 18 print("*" * 20) 19 print("进程结束了") # 查看当前进程的进程号 20 21 22 # 代码似乎这样执行才是正常的,这就是join()方法的作用 23 >>>54321 24 子进程: 10472 25 子进程的父进程: 8692 26 12345 27 ******************** 28 进程结束了
join()方法需要注意的地方
同时开启多个子进程
1 def func(a, b): 2 print(a) 3 time.sleep(3) 4 print(b) 5 6 7 if __name__ == '__main__': 8 p = Process(target=func, args=(10, 20)) 9 p.start() 10 p = Process(target=func, args=(11, 21)) 11 p.start() 12 p = Process(target=func, args=(12, 22)) 13 p.start() 14 p = Process(target=func, args=(13, 23)) 15 p.start() 16 17 print("*" * 20) 18 # join()之上的程序是异步的,join()之后的方法变成了同步 19 p.join() 20 print("进程结束了") # 查看当前进程的进程号 21 22 23 >>>******************** 24 10 25 11 26 12 27 13 28 20 29 21 30 22 31 23 32 进程结束了
使用for循环开启多个子进程
for 循环配合p.join()方法值得注意的地方
1 import os 2 import time 3 from multiprocessing import Process 4 5 6 def func(a, b): 7 print("*" * a) 8 time.sleep(3) 9 print("*" * b) 10 11 12 if __name__ == '__main__': 13 14 for i in range(10): 15 p = Process(target=func, args=(5, 10)) 16 p.start() 17 18 print("*" * 20) 19 # join()之上的程序是异步的,join()之后的方法变成了同步 20 p.join() 21 print("进程结束了") # 查看当前进程的进程号 22 23 >>>***** 24 ***** 25 ***** 26 ***** 27 ***** 28 ***** 29 ***** 30 ******************** 31 ***** 32 ***** 33 ***** 34 ********** 35 ********** 36 ********** 37 ********** 38 ********** 39 ********** 40 ********** 41 ********** 42 进程结束了 # 出问题了 43 ********** 44 **********
1 import time 2 from multiprocessing import Process 3 4 5 def func(a, b): 6 print("*" * a) 7 time.sleep(6) 8 print("*" * b) 9 10 11 if __name__ == '__main__': 12 p_list = [] 13 for i in range(5): 14 p = Process(target=func, args=(5, 10)) 15 p_list.append(p) 16 p.start() 17 [p.join() for p in p_list] 18 print("进程结束了") # 查看当前进程的进程号 19 20 >>>***** 21 ***** 22 ***** 23 ***** 24 ***** 25 ********** 26 ********** 27 ********** 28 ********** 29 ********** 30 进程结束了
[p.join() for p in p_list] 的引用场景
1 # 需求想500个文件里面写数据,用异步的方式实现 2 import os 3 from multiprocessing import Process 4 5 6 def func(file_name, contents): 7 with open(file_name, 'w') as f: 8 f.write(contents * '+') 9 10 11 if __name__ == '__main__': 12 p_list = [] 13 for i in range(5): 14 p = Process(target=func, args=("info{0}".format(i), i)) 15 p_list.append(p) 16 p.start() 17 [p.join() for p in p_list] 18 print([i for i in os.walk(r'G:进线程')])
开启多进程的第二种方式 —— 继承
最简易的
1 import os 2 from multiprocessing import Process 3 4 5 # 开启多进程的第二种方式 6 class MyProcess(Process): 7 def run(self): 8 print(os.getpid()) 9 10 11 if __name__ == '__main__': 12 print("主:", os.getpid()) 13 p1 = MyProcess() 14 p1.start() 15 p2 = MyProcess() 16 p2.start() 17 18 >>>主: 7824 19 12324 20 14660
1 # Process 类中的属性一览 2 3 class Process(object): 4 def __init__(self, group=None, target=None, name=None, args=(), kwargs={}): 5 self.name = '' # 进程名 6 self.daemon = False # 7 self.authkey = None # 8 self.exitcode = None 9 self.ident = 0 10 self.pid = 0 # 进程号 11 self.sentinel = None 12 self.is_alive = '' # 判断子进程是否活着 13 self.terminate = '' # 结束一个子进程
升级,传参数
1 import os 2 from multiprocessing import Process 3 4 5 # 开启多进程的第二种方式 6 class MyProcess(Process): 7 def __init__(self, args1, args2): 8 super(MyProcess, self).__init__() 9 self.arg1 = args1 10 self.arg2 = args2 11 12 def run(self): 13 print(self.pid) 14 print(self.name) 15 print(self.arg1) 16 print(self.arg2) 17 18 19 if __name__ == '__main__': 20 print("主:", os.getpid()) 21 p1 = MyProcess(10, 20) 22 p1.start() 23 p2 = MyProcess(11, 22) 24 p2.start() 25 26 >>>主: 6916 27 9288 28 MyProcess-1 29 10 30 20 31 7412 32 MyProcess-2 33 11 34 22
多进程之间数据是隔离的
-
进程与进程之间数据是隔离的
-
父进程与子进程之间的数据也是隔离的
1 from multiprocessing import Process 2 3 def work(): 4 global n 5 n=0 6 print('子进程内: ',n) 7 8 9 if __name__ == '__main__': 10 n = 100 11 p=Process(target=work) 12 p.start() 13 p.join() # 执行玩子进程之后在执行父进程 14 print('主进程内: ',n) 15 16 >>>子进程内: 0 17 主进程内: 100 18 19 # 在子进程中设置了全局变量但是在主进程中的n的值并没有发生改变,得出结论:主进程与子进程之间的数据也是隔离的
使用socket实现聊天并发
# server端 from socket import * from multiprocessing import Process server=socket(AF_INET,SOCK_STREAM) server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind(('127.0.0.1',8080)) server.listen(5) def talk(conn,client_addr): while True: try: msg=conn.recv(1024) if not msg:break conn.send(msg.upper()) except Exception: break if __name__ == '__main__': #windows下start进程一定要写到这下面 while True: conn,client_addr=server.accept() p=Process(target=talk,args=(conn,client_addr)) p.start()
# client端 from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while True: msg=input('>>: ').strip() if not msg:continue client.send(msg.encode('utf-8')) msg=client.recv(1024) print(msg.decode('utf-8'))
守护进程
什么是守护进程?
守护进程是一种新的进程,自己开的一个子进程转换成的。当在一个主进程中开启子进程的时候,想要子进程随着主进程的结束而结束,那么就要将子进程变成一个守护进程。
关键字子进程转换成的,那么该如何转换啦?
先看一个现象,代码如下
import time from multiprocessing import Process # 守护进程 def func(): while 1: time.sleep(0.5) print('2222') if __name__ == '__main__': p = Process(target=func) # p.deamon = True p.start() i = 0 while i < 4: print('11111111') time.sleep(1) i += 1 # 查看输出的结果 >>>11111111 2222 11111111 2222 2222 11111111 2222 2222 11111111 2222 2222 2222 2222 2222 ...
上面的2会一直执行下去。这显然不是我们希望看见的效果,我希望当主进程执行结束之后,子进程也会跟着结束。守护进程就是做这件事的。如何来设置守护进程 。很简单加上一段代码!
1 import time 2 from multiprocessing import Process 3 4 5 # 守护进程 6 def func(): 7 while 1: 8 time.sleep(0.5) 9 print('2222') 10 11 12 if __name__ == '__main__': 13 p = Process(target=func) 14 p.daemon = True # 在start之前加 15 p.start() 16 i = 0 17 while i < 4: 18 print('11111111') 19 time.sleep(1) 20 i += 1 21 22 >>>11111111 23 2222 24 11111111 25 2222 26 2222 27 11111111 28 2222 29 2222 30 11111111 31 2222 32 2222
改进:添加守护进程
1 import time 2 from multiprocessing import Process 3 4 5 def func(): 6 while 1: 7 time.sleep(0.5) 8 print('2222') 9 10 11 if __name__ == '__main__': 12 p = Process(target=func) 13 p.daemon = True # 在start之前加 14 p.start() 15 i = 0 16 while i < 4: 17 print('11111111') 18 time.sleep(1) 19 i += 1 20 21 >>>11111111 22 2222 23 11111111 24 2222 25 2222 26 11111111 27 2222 28 2222 29 11111111 30 2222 31 2222
有上面的现象引出结论:
- 主进程会随着子进程代码的结束而结束(子进程如果是个死循环,那么程序会一致执行下去)
- 守护进程会随着 主进程的代码的执行完毕,而结束。
- p.terminate() 结束一个子进程,并不会立即生效。
进程锁
什么叫做进程锁?
进程锁适用于多进程的程序,就是将异步的程序变成同步的
模拟一个现象来引出锁的概念。
春运到了大家都在抢火车票。100个人抢一张火车票(并发的过程)。但最后只能卖出去一张(心塞~)。
买票的过程用代码来表示就是这样的
#文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 #并发运行,效率高,但竞争写同一文件,数据写入错乱 from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db')) print('