目录
并发编程(上)
操作系统发展史
操作系统的发展史
穿孔卡片:
一个计算机机房,一次只能被一个卡片使用.
缺点:CPU利用率最低.
联机批处理系统:
支持多用户去使用一个计算机机房.
脱机批处理系统:
高速磁盘:提高文件的读取速度
优点:提高CPU的利用率
进程基础
进程是对正在运行程序的一个抽象.
操作系统的作用
隐藏丑陋复杂的硬件接口,提供良好的抽象接口
管理,调度进度,并且将多个进程对硬件的竞争变得有序
多道技术(基于单核情况下研究):
单道:多个使用使用CPU时是串行.
多道技术:
空间上的复用-->一个CPU可以提供给多个用户去使用.
时间上的复用-->切换 + 保存状态
IO操作:input();print();time.sleep(3)
1 若CPU遇到IO操作,会立即将当前执行程序CPU使用权断开
2 若一个程序使用CPU的时间过长,会立即将当前执行程序CPU使用权断开.
特点:程序的执行率降低
进程调度
先来先服务调度算法
a,b程序,若A先来,先占用CPU;缺点是程序A先使用,程序B必须等待程序A使用CPU结束后才能使用.
短作业优先调度算法
a,b程序,谁的用时短,先优先调度使用CPU;缺点是若程序A使用时间长,有N个程序使用时间短.必须等待所有短的程序结束后才能使用.
时间片轮转法
CPU执行的时间一秒钟,加载N个程序,将要一秒等分成N个时间片.
多级反馈队列
将执行优先分为多层级别.
一级;优先级最高
二级;优先级第二,依次类推
并发与并行
==并行==
并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核CPU)
==并发==
并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A,交替使用,目的是提高效率.
同步异步阻塞非阻塞
同步
若有两个任务需要提交,在提交第一个任务时,必须等待该任务执行结束后,才能继续提交并执行第二个任务
异步
若两个任务需要提交,在提交第一个任务时,不需要原地等待,立即可以提交并执行第二个任务
阻塞
阻塞态,遇到IO一定会阻塞(凡是遇到IO操作的进程,都会进入阻塞态.若IO结束,必须重新进入就绪态.
非阻塞
就绪态(所有进程创建时都会进入就绪态,准备调度)
运行态(调度后的进程,进入运行态)
创建进程的两种方式
==UNIX和Windows创建进程==
- 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的映像,同样的环境字符串和同样的打开文件(在shell解释器进程中,执行一个命令就会创建一个子进程)
- 在Windows中该系统调用是: createprocess, createprocess既处理进程的创建.也负责八正确的程序装入新进程.
关于创建子进程,UNIX和Windows:
- 相同的是: 进程创建后,父进程和子进程有各自不同的地址空间,(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程在其他地址空间中的修改都不会影响到另外一个进程.
- 不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的,但是对于Windows系统来说,从一开始父进程与子进程的地址空间就是不同的.
进程的结束:1.正常退出;2.出错退出;3.严重错误;4.被其他进程杀死
开启多进程(multiprocess.process)
multiprocess模块
仔细来说,multiprocess不是一个模块,而是python中一个操作,管理进程的包.由于提供的子模块非常的多,为了方便归类记忆,可以分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享.
multiprocess.process模块
process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建.
process模块
process([group [,target [,name [,args [,kwargs]]]]])
由上面类实例化得到的对象,表示一个子进程的任务(尚未启动)
强调:
需要使用关键字的方式来指定参数
args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
- group参数未使用,值始终为none
- target表示调用对象,即子进程要执行的任务
- args表示第哦啊用对象的位置参数元组,args=(q,2,'egon').
- kwargs表示调用对象的字典,kwargs={'name':'egon','age':19}
- name为子进程的名称
方法介绍:
- p.start:启动进程,并调用该子进程的p.run()
- p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
- p.terminate():强制终止进程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。
在Windows中使用process注意事项
在Windows操作系统中,由于没有fork(Linux操作系统中创建进程的机制),在创建子进程的时候会自己动import启动它的这个文件,而在import的时候又执行了整个文件。因此如果将process()直接写在文件中就会无线递归创建子进程报错,所以必须把创建子进程的部分使用if__name___=='__main__'判断保护起来,import的时候,就不会递归运行了。
使用process模块创建进程
在一个python进程中开启子进程start方法和并发效果
守护进程
会随着主进程的结束而结束
主进程创建守护进程
- 守护进程会在主进程代码执行结束后九终止、
- 守护进程内无法再开启子进程,否则抛出异常
socket并发实例
使用多进程实现socket聊天并发送——server端
使用多进程实现socket聊天并发——client端
多进程中其他方法
进程对象的其他方法:terminate和is_alive
进程对象的其他属性:pid和name
进程同步(multiprocess.lock)
通过学习了异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用io资源,但是也给我们带来了新的问题:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。
多进程抢占输出资源
import os import time import random from multiprocessing import process def wrok(n): print('%s: %s is runing' %(n,os.getpid())) time,sleep(random.random) print('%s: %s is done' %(n,os.getpid())) if __name__ == '__main__': for i in range(3): p=process(target=work,args=(i,)) p.start
使用锁维护执行顺序
import os import time import random from multiprocess import process,lock def work(lock,n): lock.acquire() print('%s: %s runing' %(n,os.getpid())) time.sleep(random.random()) print('%s: %s is done' %(n,os.getpid())) lock.release() if __name__=='__main__': lock=lock() for i in range() p=process(targs=work,args=(lock,i)) p.start()
上述这种情况虽然使用加锁的形式实现了顺序的执行,但是程序又重新变成串行了,这样确实会浪费了时间,却保证了数据的安全。
使用锁来保证数据安全
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以使用文件共享数据实现进程间的通信,但问题是:
- 效率低(共享数据基于文件,而文件是硬盘上的数据)
- 需要自己加锁处理
因此我们最好找寻一种解决方案能够兼顾:
- 效率高(多个进程共享一块内存的数据)
- 帮我们处理好加锁问题。这就是mutiprocessing模块为我们提供的基于消息的ipc通信机制:队列和管道。
队列和管道都是将数据存放于内存中,队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。