昨日作业:
服务端:
服务端: from socket import * from multiprocessing import Process def server(ip,port): server = socket(AF_INET, SOCK_STREAM) server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) server.bind((ip,port)) server.listen(5) while True: conn, addr = server.accept() print(addr) # communicate(conn) p = Process(target=communicate, args=(conn,)) p.start() server.close() def communicate(conn): while True: try: data=conn.recv(1024) if not data:break conn.send(data.upper()) except ConnectionResetError: break conn.close() if __name__ == '__main__': server('127.0.0.1', 8090)
客户端:
from socket import * client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8090)) while True: msg=input('>>: ').strip() if not msg:continue client.send(msg.encode('utf-8')) data=client.recv(1024) print(data.decode('utf-8'))
os模块下的进程id:
print(os.getpid(), os.getppid()) # 这里的getpid 是当前进程的id地址 time.sleep(200) # 得到的结果是(5740,3272) # 5740是当前的进程的id地址,而后面的3272则是pycharm的id地址, # 因为程序是在pycharm里面执行的,最终结果是在cmd解释器里面运行 # 每次重新运行进程就是得到一个新的进程,所以,进程id是会有变化 # 同理,pycharm如果重新进入,一样会改变pycharm的id,
上节内容回顾:
1 什么是进程?
进程是一个抽象的概念,进程即正在执行的过程,进程的概念起源于操作系统,
进程的创建,调度管理都归操作系统管
2 操作系统的作用?
1、管理硬件,把复杂丑陋的硬件接口封装成良好的接口给用户使用
2、进程的创建,调度管理都归操作系统管
3 多道技术?
产生背景:针对单核下实现并发
核心:
1、空间上复用(多个进程的内存空间是互相隔离的)
2、时间上复用(复用cpu的时间,)
4 开启进程
from multiprocessing import Process
import time
def task(name):
print('%s is running' %name)
time.sleep(2)
if __name__ == '__main__': #在windows系统下,开子进程的代码必须写到这一行下面
p=Process(target=task,args=('egon',))
p.start() #只是在给操作系统发了一个信号,让操作系统去开进程(申请内存+拷贝父进程的地址空间)
print('主')
================================================================================================
from multiprocessing import Process
创建进程的类:
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
在windows中Process()必须放到# if __name__ == '__main__':下
进程直接的内存空间是隔离的
开启进程的两种方式
#方式一: # from multiprocessing import Process # import time # # def task(name): # print('%s is running' %name) # time.sleep(2) # # if __name__ == '__main__': #在windows系统下,开子进程的代码必须写到这一行下面 # p=Process(target=task,args=('egon',)) # p.start() #只是在给操作系统发了一个信号,让操作系统去开进程(申请内存+拷贝父进程的地址空间) # print('主') ================================================================================================== #方式二: from multiprocessing import Process import time class Myprocess(Process): def __init__(self,name): super().__init__() self.name=name def run(self): time.sleep(3) print('%s is running' % self.name) time.sleep(2) if __name__ == '__main__': # 在windows系统下,开子进程的代码必须写到这一行下面 p = Myprocess('egon') p.start() # p.run() print('主')
start方法:{
一种是使用Process开启进程
一种是使用Myprocess开启进程}
属性和方法:
我们的join用法是很重要的用法,还有start()也很重要启动子进程,
join用法是为了回收子进程,等待所有的子进程回收后才会去执行父进程,然后结束程序,这样就可以避免掉子进程没有被回收然后沦落为孤儿进程或者僵尸进程的情况。
from multiprocessing import Process import time,random def piao(name): print('%s is piaoing' %name) time.sleep(random.randint(1,3)) print('%s is done' %name) if __name__ == '__main__': # p1=Process(target=piao,args=('alex',)) # p2=Process(target=piao,args=('wxx',)) # p3=Process(target=piao,args=('yxx',)) # # p1.start() # p2.start() # p3.start() # # p3.join() # join(p) # p1.join() #join(p) # p2.join() #join(p) # # print('主') p1=Process(target=piao,args=('alex',)) p2=Process(target=piao,args=('wxx',)) p3=Process(target=piao,args=('yxx',)) p_l=[p1,p2,p3] for p in p_l: p.start() for p in p_l: p.join() print('主') # import time,os # print(os.getpid(),os.getppid()) # time.sleep(1000) #python3 test.py --->python.exe #其他属性或方法 from multiprocessing import Process import time, random def task(): print('孙子运行了') time.sleep(3) def piao(name): print('%s is piaoing' % name) time.sleep(random.randint(1, 3)) print('%s is done' % name) p=Process(target=task,) p.start() if __name__ == '__main__': p1=Process(target=piao,args=('alex',),name='xxxxxxxx') p1.start() # print(p1.name) # print(p1.pid) # p1.join() # p1.terminate() # print(p1.is_alive()) # p1.join(timeout=1) print('主')
join用法
from multiprocessing import Process import time import random def piao(name): print('%s is piaoing' %name) time.sleep(random.randint(1,3)) print('%s is piao end' %name) p1=Process(target=piao,args=('egon',)) p2=Process(target=piao,args=('alex',)) p3=Process(target=piao,args=('yuanhao',)) p4=Process(target=piao,args=('wupeiqi',)) p1.start() p2.start() p3.start() p4.start() #有的同学会有疑问:既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗? #当然不是了,必须明确:p.join()是让谁等? #很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p, #详细解析如下: #进程只要start就会在开始运行了,所以p1-p4.start()时,系统中已经有四个并发的进程了 #而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键 #join是让主线程等,而p1-p4仍然是并发执行的,p1.join的时候,其余p2,p3,p4仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join.p4.join直接通过检测,无需等待 # 所以4个join花费的总时间仍然是耗费时间最长的那个进程运行的时间 p1.join() p2.join() p3.join() p4.join() print('主线程') #上述启动进程与join进程可以简写为 # p_l=[p1,p2,p3,p4] # # for p in p_l: # p.start() # # for p in p_l: # p.join()
当进程中有子进程的时候,会先执行父进程然后再执行子进程, 这样就会出现如下的情况(产生僵尸进程或者孤儿进程,占用内存空间)
进程回收机制,这里有两个概念,孤儿进程(当你的主进程把自己主进程该干的活运行完之后会回来等子进程运行完后回收子进程,但是有一种操作会杀死父进程(如taskkill),你就变成了孤儿进程,没有父进程来回收子进程,但是这里会有类似福利院的机制,就是系统内部的回收机制,当然了回收的会很慢,在被系统内部的回收机制回收之前该子进程都会占用内存。)。僵尸进程(此类进程是没有父进程回收它,也没有福利院机制回收它。代码运行完后cpu会立即被释放出来,但是进程依然存留在内存空间中,内存空间的占用需要被释放掉,正常来说子进程会被父进程回收,如果父进程意外身亡(写代码的过程中不应该出现在子进程被回收之前把父进程写死,但是技术不济就会出现这类问题),就会有系统内部的回收机制来处理它把它回收掉,如果遇到系统内部的回收机制出现故障,那么就没有机制来回收该进程,它就变成了僵尸进程,会一直占用内存,此时就会很危险,内存一直被占用会影响系统运行速度)
进程池:
''' 提交/调用任务的方式有两种: 同步调用:提交/调用一个任务,然后就在原地等着,等到该任务执行完毕拿到结果,再执行下一行代码 异步调用: 提交/调用一个任务,不在原地等着,直接执行下一行代码,结果? ''' # # from multiprocessing import Process,Pool # from concurrent.futures import ProcessPoolExecutor # import time,random,os # # def piao(name,n): # print('%s is piaoing %s' %(name,os.getpid())) # time.sleep(1) # return n**2 # # if __name__ == '__main__': # p=ProcessPoolExecutor(4) # objs=[] # start=time.time() # for i in range(10): # # res=p.submit(piao,'alex %s' %i,i).result() #同步调用 # # print(res) # obj=p.submit(piao,'alex %s' %i,i) #异步调用 # objs.append(obj) # # for obj in objs: # print(obj.result()) # # stop=time.time() # print(stop-start) # # 关门+等 # # pool.close() # # pool.join() # p.shutdown(wait=True) # print('主',os.getpid()) # from multiprocessing import Process,Pool from concurrent.futures import ProcessPoolExecutor import time, random, os def piao(name, n): print('%s is piaoing %s' % (name, os.getpid())) time.sleep(1) return n ** 2 if __name__ == '__main__': p = ProcessPoolExecutor(4) objs = [] start = time.time() for i in range(10): # res=p.submit(piao,'alex %s' %i,i).result() #同步调用 # print(res) obj = p.submit(piao, 'alex %s' % i, i) # 异步调用 objs.append(obj) p.shutdown(wait=True) print('主', os.getpid()) for obj in objs: print(obj.result()) stop = time.time() print(stop - start)
今天的总结,进程的两种开启方式,一种是使用process,定义一个函数,另一种是使用myprocess,定义一个类,类里面必须要有run方法
这两种方法子进程必须要写在(在windows系统里是这样)
if __name__ == '__main__':
有start(开启一个子进程)和join方法(回收一个子进程),
还有就是process属性的一些其他方法和属性,有terminate,删除进程,pid是查看当前进程的ID地址
is_alive,查看当前进程是否是还存在的,
再就是关于进程池的概念,所谓的进程池就是相当于一个小池子,它是为了划分范围的,应用场景就是:
当我们的服务端跟客户端链接的时候,服务端是一个,它要跟多个客户端链接,它不可能同时连接上无上限的客户端,它的系统会崩溃,
但是也不能一次链接一个客户端,这样当然不会崩,但是效率也太低了,所以由于硬件的受限,我们要在有限的硬件条件下实现效率最大化,
那么就需要划定一个范围,在这个范围内我们可以在能承受的范围内实现效率的最大化.这就是我们的进程池的概念,
在进程池的概念中还有同步和异步:
在同步调用中提交一个任务或者调用一个任务的时候,父进程就在原地等待着,等到子进程执行完之后把它回收过来拿到一个结果
然后再执行下一行代码,得到的结果就是所有的进程执行累加的时间,这样的效率会很低,如果我们使用进程池就不会用这种方法,使用进程池就是为了在大量的
数据处理中可以提高效率用的.
而在异步调用中提交一个任务或者调用的时候,父进程不会在原地等着,直接执行下一行代码,然后最后把所有的子进程的结果统一返回
这样的执行效率就更高了,就像昨天学的实现了时间和空间上的复用.这样最后的执行时间就是最长时间的那个进程的时间,而不是所有的进程累加的时间