并发编程
---恢复内容开始---
首先要了解的知识点:
1、什么是程序?什么是进程?
程序说白了就是一堆文件
进程就是一个正在执行的过程/程序
所以说进程是一个抽象的概念
这个概念起源操作系统
2、什么是操作系统
定义:
操作系统是位于计算机硬件与应用软件之间
用于协调、管理、控制计算机硬件与软件的资源的
一种控制程序
3、操作系统的两大作用:
1、把复杂丑陋的硬件操作都封装成美丽的接口,提供给应用程序使用
2、把进程对硬件的竞争变的有序
4、串行:把几个程序从硬盘读到内存,然后cpu从内存一条条读取程序,一条条执行程序,本质上也是一个个完成程序
5、多道技术
多道的产生背景是想要在单个cpu的情况下实现多个进程并发执行的效果
1、空间上的复用 (多道程序复用内存的空间,程序与程序在内存的空间是相互隔离的,是物理上的隔离)
2、时间上的复用 (多道程序复用CPU时间,实现并发)
当cpu遇到IO操作要切换(提升效率)
一个进程占用cpu时间过长也切(降低效率)
进程与进程之间的内存空间是互相隔离的
并行与并发:
并行是同一时间执行多道程序,这只存在与多个cpu的情况下,单个cpu只能实现并发。无论是并发还是并行,在用户看来都是'同时’运行的,因为cpu的
运算时间极快所以我们肉眼根本无法发现是不是同时运行。
并发:单核+多道技术就可以实现并发,是伪并行
并行:只有具有多个cpu才能实现
基于UDP协议实现并发:
udp不需要经过3次握手和4次挥手,不需要建立链接,直接发数据就可以了。
TCP与udp的区别:tcp需要建立链接,发送数据必须要收到对方确认收到才能继续发送数据,而且不能单方面断链接,优点是可靠,数据不易丢失
udp不需要建链接,客户端只需要发数据,不需要确认服务端是否收到,也可以单方面断链接,优点是发送效率高,缺点是容易丢失数据
#服务端
import socket server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议 server.bind(('127.0.0.1',8080)) while True: client_data,client_addr=server.recvfrom(1024) msg=input('回复%s:%s>>>:' %(client_addr[0],client_addr[1])) server.sendto(msg.encode('utf-8'),client_addr)
#客户端1
import socket client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议 while True: msg=input('>>>: ').strip() client.sendto(msg.encode('utf-8'),('127.0.0.1',8080)) res,server_addr=client.recvfrom(1024) print(res.decode('utf-8'))
客户端2
import socket client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 数据报协议 while True: msg=input('>>>: ').strip() client.sendto(msg.encode('utf-8'),('127.0.0.1',8080)) res,server_addr=client.recvfrom(1024) print(res.decode('utf-8'))
进程:
进程的创建:process模块
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称
方法介绍:
p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
属性介绍:
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
开启子进程的两种方式:
#第一种:直接定义方法,用函数的形式(一般用这种方法)
from multiprocessing import Process import time def task(name): print('%s is running' %name) time.sleep(3) print('%s is done' %name) if __name__ == '__main__': # 在windows系统之上,开启子进程的操作一定要放到这下面 # Process(target=task,kwargs={'name':'egon'}) p=Process(target=task,args=('egon',)) p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态 print('======主') #第二种:用类方法 from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super(MyProcess,self).__init__() self.name=name def run(self): print('%s is running' %self.name) time.sleep(3) print('%s is done' %self.name) if __name__ == '__main__': p=MyProcess('egon') p.start() print('主')
进程间的空间是隔离的:
from multiprocessing import Process n=100 #在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了 def work(): global n n=0 print('子进程内: ',n) if __name__ == '__main__': p=Process(target=work) p.start() #隔离方法 print('主进程内: ',n)
join方法:回收子进程的资源
from multiprocessing import Process
import time
import random
class Piao(Process):
def __init__(self,name):
self.name=name
super().__init__()
def run(self):
print('%s is piaoing' %self.name)
time.sleep(random.randrange(1,3))
print('%s is piao end' %self.name)
p=Piao('egon')
p.start()
p.join(0.0001) #等待p停止,等0.0001秒就不再等了
print('开始')
join:主进程等,等待子进程结束