Liunx编程-python进程
什么是多任务
-
程序中的多任务
- 如果有多件事情是同时进行,我们把这种情况叫做多任务。Linux 是真正的多任务、多用户操作系统,windows是多任务操作系统,但不是真正意义上的多用户操作系统。
-
并发和并行
-
时间片轮转
- 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称RR调度。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间优先数调度算法常用于批处理系统中。在进程调度中,每次调度时,系统把处理机分配给就绪队列中优先数最高的进程。它又分为两种:==非抢占式优先数算法和抢占式优先数算法。==
- ==非抢占式优先权算法==:系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成
- ==抢占式优先权调度算法==:系统同样把处理机分配给优先权最高的进程,使之执行.但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程
- ==系统的进程会比开发者开发的优先调度==
-
并发和并行
- 并发:在多核系统里的,同时执行多个进程,一般会情况下会有些进程没有机会执行,这种情况是并发。
- Concurrent(并发)
- Parallel(并行)
-
多任务在程序中的实现–进程和线程
- 多任务系统中有3个功能单位:任务、进程和线程
-
os.fork函数创建进程
-
程序和进程区别
-
程序:编写完毕的代码,在没有运行的时候,称之为程序(代码)
-
进程:正在运行着的代码,就成为进程,进程占用了内存和cpu等资源。
-
进程,除了包含代码以外,还有需要运行的环境等,所以和程序(代码)是有区别的.
-
fork( )
-
注意,fork函数,只在Unix/Linux/Mac上运⾏,windows不可以
-
fork函数的使用
-
PID(进程控制符)
-
PID是各进程的身份标识,程序一运行系统就会自动分配给
-
返回三个值0,大于0的值,-1
-
#如果返回0,当前是子进程
-
#如果返回大于0,当前是父进程
-
fork函数原理
-
fork下的函数体父子各一份
-
普通的函数调⽤,调⽤⼀次,返回⼀次,但是fork()调⽤⼀次,返回两次,因 为操作系统⾃动把当前进程(称为⽗进程)复制了⼀份(称为⼦进程),然 后,分别在⽗进程和⼦进程内返回。 ⼦进程永远返回0,⽽⽗进程返回⼦进程的ID。
-
这样做的理由是,⼀个⽗进程可以fork出很多⼦进程,所以,⽗进程要记下 每个⼦进程的ID,⽽⼦进程只需要调⽤getppid()就可以拿到⽗进程的ID。
getpid()、getppid()
-get pid()当前进程的Pid os.getppid()当前父进程的pid
- 子进程永远返回0,而父进程返回子进程的ID,这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID,getpid()得到当前进程的ID。
小结
进程与进程之间数据不共享
- 进程间数据不同享
- fork时子进程获得父进程代码和数据段、共享库、堆和栈的复制,所以变量的地址也是一样的
- 多进程中,每个进程中所有数据(包括全局变量)都各有拥有一份,互不影响。 fork时子进程获得父进程代码和数据段、共享库、堆和栈的拷贝,所以变量的地址也是一样的,并且在某一个进程中修改相同变量的值,在另外一个进程中的相同变量的值不会修改。
-
多次fork问题
-
父子和子进程的执行顺序
- 父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法
- 注意在pycharm上要使用sleep休眠一会,等待子进程执行在推出,次退出主进程,否则子进程没有机会执行
-
扩展-for炸弹
创建跨平台的进程–==multiprocessing==
-
Windows无法使用fork调用创建进程,那在windows中怎么创建进程呢?由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持,multiprocessing模块就是跨平台版本的多进程模块,使用改模块的Process类可以创建进程
-
使用Process创建进程
-
如果时全局变量子进程与主进程互不干扰
-
join等待子进程结束
-
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例对象,用start()方法启动,这样创建进程比fork()还要简单。
-
join 让你的主进程让步
-
p.join()
-
join()没加参数是指子进程执行完毕之后,主进程才可以加入 join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
-
子进程只要是完成了“目标”,就自杀
-
如果进程没有完成任务,可用他杀调用p.terminate()
-
Process语法结构
-
Process([group [, target [, name [, args [, kwargs]]]]])
-
target:表示这个进程实例所调用对象;
-
args:表示调用对象的位置参数元组;
-
kwargs:表示调用对象的关键字参数字典;
-
name:为当前进程实例的别名;
-
group:大多数情况下用不到;
-
p.terminate()不管任务是否完成,立即终止
-
is_alive()判断进程是否存活
-
join([timeout]):是否等待进程实例执行结束,或等待多少秒;
-
start():启动进程实例(创建子进程);
-
run():如果没有给定target参数,对这个对象调用-
-
start()方法时,就将执行对象中的run()方法;
-
terminate():不管任务是否完成,立即终止;
-
Process类常用属性:
-
name:当前进程实例别名,默认为Process-N,N为从1开始递增的整数;
-
pid:当前进程实例的PID值;
进程的创建–自定义Process子类
-
自定义Process子类
- 只要启动start自动调用类里的run方法
- run不能手动调用
- ==只要时创建进程子类,必须重写run方法==
- 如果自己定义了__ init__就必须调用父类的super().__ init__()
-
进程池(Pool)
- ultiprocessing模块提供的Pool方法。
- 阻塞式:添加一个执行一个,执行完毕,才并且返回,然后接着添加下一个
- 非阻塞式:全部添加到队列,立刻返回,并没有等他执行完毕,当执行完毕后有回掉,回掉种发携带结果
-
from multiprocessing import Pool
-
apply_async非阻塞式
- apply_async(func[, args[, kwds]])
- 用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
- pool.apply_async(func,args=(tuple),callback=)
- 如果你的回掉函数有参数,是通过当前任务的返回值
- callback=回掉函数
- pool.close()#不再添加其他的进程
- 进程池:最大是Tool(n)n
- n个只要有一个结束了,进程池立马会将释放的进程分配,依此类推
- 只要一个进程结束,扔出一个回掉函数,父进程立刻调用callback得到一个返回值
-
apply堵塞式
- 阻塞式没有回掉函数
- apply(func[, args[, kwds]]):使用阻塞方式调用func
-
multiprocessing.Pool常用函数解析
- apply_async(func[, args[, kwds]]) :使用非阻塞方式调用func(并行执行,堵塞方式必须等待上一个进程退出才能执行下一个进程),args为传递给func的参数列表,kwds为传递给func的关键字参数列表;
- apply(func[, args[, kwds]]):使用阻塞方式调用func
- close():关闭Pool,使其不再接受新的任务;
- terminate():不管任务是否完成,立即终止,针对的是非阻塞式。
- join():主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用;
-
阻塞和非阻塞的区别
- 应用进程请求i/o操作(recv)时,如果数据未准备好请求是否立即返回
- 不立即返回:阻塞式
- 立即返回:非阻塞式
- 阻塞式:任务一起交给pool则pool立刻打印全部
- 非阻塞式:排队进入每次只能执行一个
-
使用Queue实现进程间通信
-
Python中主要使用Queue(队列)、Manage支持管道通信、套接字( socket )等等实现进程间通信。
-
Queue的使用
-
Python中在multiprocessing模块的Queue类可以实现进程之间的数据传递,Queue本身是一个消息列队程序
-
from multtiprocessing import Quese
-
Queue.empty()在不同平台有差异
-
Queue的参数说明
-
初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的耗尽);
-
Queue.qsize():返回当前队列包含的消息数量
-
Queue.empty():如果队列为空,返回True,反之False
-
Queue.full():如果队列满了,返回True,反之False;
-
.Queue.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常; 2)如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常
-
Queue.get_nowait():相当Queue.get(False);
-
.Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True;(阻塞式)如果block使用默认值,且没有设置timeout(单位秒),消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常; 2)如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
-
Queue.put_nowait(item):相当Queue.put(item, False);
-
进程池中的使用Manager()中的Queue
-
from multiprocessing import Manager
-
没有长度限制
-
如果使用的式进程池,则队列 queue=Menage().Queue()
-
不能使用queue.Queue()
-
打印进度
-
\r 换行,光标再上一行
-
%’%%‘输出一个单一的%