标签:sel process 数据 默认值 init val 实现类 timeout 操作系统
实现类似唱歌又跳舞的任务同时进行的,叫做:多任务
什么叫“多任务”。简单地说, 就是操作系统可以同时运行多个任务。
单核CPU执行多任务,就是操作系统轮流让各个任务交替执行,任务1执行0.01秒, 切换到任务2,任务2执行0.01秒,再切换到任务3, 执行0.01秒……这样反复执行下去。每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了, 我们感觉就像所有任务都在同时执行一样。
真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
编写完毕的代码,在没有运行的时候,称之为程序。
正在运行着的代码,就成为进程。
进程,除了包含代码以外,还有需要运行的环境等,所以和程序是有区别的。
os模块封装了fork函数,可以在Python程序中轻松创建子进程
import os
#注意,fork函数,只在Unix/Linux/Mac上运行,Windows不可以
pid = os.fork()
if pid == 0:
print(‘哈哈111‘)
else:
print(‘哈哈222‘)
说明
可以理解为getppid——get parents pid 的缩写
import os
rpid = os.fork()
if rpid<0:
print("fork调用失败。")
elif rpid == 0:
print("我是子进程(%s),我的父进程是(%s)"%(os.getpid(),os.getppid()))
else:
print("我是父进程(%s),我的子进程是(%s)"%(os.getpid(),rpid))
print("父子进程都可以执行这里的代码")
运行结果:
我是父进程(19360),我的子进程是(19361)
父子进程都可以执行这里的代码
我是子进程(19361),我的父进程是(19360)
父子进程都可以执行这里的代码
#coding=utf-8
import os
import time
num = 0
# 注意, fork函数, 只在Unix/Linux/Mac上运行, windows不可以
pid = os.fork()
if pid == 0:
num+=1
print(‘哈哈1---num=%d‘%num)
else:
time.sleep(1)
num+=1
print(‘哈哈2---num=%d‘%num)
总结:
如果在一个程序,有2次的fork函数调用, 是否就会有3个进程呢?
#coding=utf-8
import os
import time
# 注意,fork函数,只在Unix/Linux/Mac上运行,windows不可以
pid = os.fork()
if pid == 0: #if下是子进程的操作,因为子进程的返回值永远是0
print(‘haha...1,我是%s,我的父进程是%s‘%(os.getpid(),os.petppid))
else: #else下是主进程操作,因为主进程的返回值是其子进程的pid
print(‘haha...,我是主进程%s‘%os.getpid)
pid = os.fork()
if pid == 0:
print(‘haha...2,我是%s,我的父进程是%s‘%(os.getpid(),os.petppid))
else:
print(‘haha...,我是主进程%s‘%os.getpid)
time.sleep(1)
运行结果:
haha...,我是主进程9584
haha...,我是主进程9584
haha2...,我是9586,我的父进程是9584
haha1...,我是9585,我的父进程是9584
haha...,我是主进程9585
haha2...,我是9587,我的父进程是9585
仔细看上述代码和运行结果,程序的执行结果并不是你想象的打印了四次,在这里需要说明的是,当主进程(9584)执行到第一条fork语句时,要创建一个子进程,在这个时候,会将主进程的所有内容,复制一份给子进程(9585),也就说,子进程(9585)中也会包含主进程(9584)中的第二条fork语句,即子进程(9585)也会再创建子进程(9587),主进程(9584)执行到第二条fork语句时,会再创建一个子进程(9586),这样就导致了这段代码会有六次的print,这样说可能依然不是很清楚,下面画一个简单的进程关系图帮助理解:
graph LR
主进程9584-->子进程9585
子进程9585-->子进程9587
主进程9584-->子进程9586
说明:
主进程、 子进程的执行顺序没有规律, 完全取决于操作系统的调度算法
multiprocessing模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个Process类来代表一个进程对象。
Process语法结构如下:
Process([group [, target [, name [, args [, kwargs]]]]])
- target:表示这个进程实例所调用对象;
- args:表示调用对象的位置参数元组;
- kwargs:表示调用对象的关键字参数字典;
- name: 为当前进程实例的别名;
- group:多数情况下用不到;
from multiprocessing import Process
from time import sleep
#子进程
def sing(param1,param2,**kwargs):
print("%s,%s"%(param1,param2))
print(kwargs)
k = 0
while k<5:
print("sing %d..."%k)
sleep(1)
k +=1
if __name__ == ‘__main__‘:
print("main process run")
#定义子进程:唱歌
p = Process(target=sing,args=(20,"xxx"),kwargs={"key1":"mmm","key2":"nnn"})
#子进程的启动
p.start()
#p.join() 如果加上加上join主进程就不会进入while循环,要等到子进程运行结束
#不加的话,子进程与主进程互不干扰,都在执行
#主进程:玩游戏
while True:
print("gaming...")
#停止0.5秒
sleep(0.5)
print("child process is over")
print("main poress end")
运行结果:
main process run
gaming...
20,xxx
{‘key2‘: ‘nnn‘, ‘key1‘: ‘mmm‘}
sing 0...
gaming...
gaming...
sing 1...
gaming...
gaming...
from multiprocessing import Process
from time import sleep
#定义进程类,继承Process
class Music(Process):
def __init__(self,name):
#调用父类的初始化方法
#因为Process类本身也有__init__方法, 这个子类相当于重写了这个方法
#但这样就会带来一个问题,我们并没有完全的初始化一个Process类,所以就不能使用从这个
#最好的方法就是将继承类本身传递给Process.__init__方法,完成这些初始化操作
Process.__init__(self)
self.name = name
#重写Process类的run方法
def run(self):
print(self.name)
k = 0
while True:
print("sing %d..." % k)
sleep(0.5)
k += 1
if __name__ == ‘__main__‘:
print("main process run")
#创建新的进程,定义进程对象
p = Music(‘xxx‘)
#对一个不包含target属性的Process类执行start()方法
#就会运行这个类中的run()方法
p.start()
# 主进程:玩游戏
while True:
print("gaming...")
# 停止0.5秒
sleep(0.5)
运行结果:
main process run
gaming...
xxx
sing 0...
gaming...
sing 1...
gaming...
sing 2...
gaming...
……
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行
multiprocessing.Pool常用函数解析:
from multiprocessing import Pool
from time import sleep
def kill():
i=0
while i<10:
print("乔峰杀了一个挑战者...")
sleep(1)
i += 1
def attack(name):
print("%s attack..."%name)
sleep(1)
if __name__ == ‘__main__‘:
#创建进程池,最大进程数5
po = Pool(5)
#向进程池添加子进程
#Pool.apply_async(要调用的目标,(传递给目标的参数元组,))
#每次循环将会用空闲出来的子进程去调用目标
po.apply_async(kill)
#po.apply(kill)
for i in range(20):
po.apply_async(attack,(‘掌门__‘+str(i),))
#po.apply(attack,(‘zhangmen_‘+str(i),))
po.close() #关闭进程池, 关闭后po不再接收新的请求
po.join() #等待po中所有进程执行完成, 必须放在close语句之后
#po.terminate()
执行代码:
乔峰杀了一个挑战者...
掌门__0 attack...
掌门__1 attack...
掌门__2 attack...
掌门__3 attack...
乔峰杀了一个挑战者...
掌门__4 attack...
掌门__5 attack...
掌门__6 attack...
掌门__7 attack...
……
如果不使用 po.apply_async
而使用 po.apply
其代码执行如下:
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
乔峰杀了一个挑战者...
掌门__0 attack...
掌门__1 attack...
掌门__2 attack...
……
Process之间有时需要通信,操作系统提供了很多机制来实现进程间的通信。可以使用multiprocessing模块的Queue实现多进程之间的数据传递,Queue本身是一个消息列队程序。
说明:
1、初始化Queue()对象时(例如:q=Queue()),若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);
2、Queue.qsize():返回当前队列包含的消息数量;
3、Queue.empty():如果队列为空,返回True, 反之False ;
4、Queue.full():如果队列满了,返回True,反之False;
5、Queue.get([block[,timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True;Queue.get_nowait():相当Queue.get(False);
-----a) 如果block使用默认值, 且没有设置timeout( 单位秒) , 消息列队如果为空, 此时程序将被阻塞( 停在读取状态) , 直到从消息列队读到消息为止,如果设置了timeout, 则会等待timeout秒, 若还没读取到任何消息, 则抛出"Queue.Empty"异常;
-----b) 如果block值为False, 消息列队如果为空, 则会?刻抛出"Queue.Empty"异常;
6、Queue.put(item,[block[,timeout]]):将item消息写入队列,block默认值为True;Queue.put_nowait(item):相当Queue.put(item, False);
-----a) 如果block使用默认值, 且没有设置timeout( 单位秒) , 消息列队如果已经没有空间可写入, 此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止,如果设置了timeout,则会等待timeout秒, 若还没空间,则抛出"Queue.Full"异常;
-----b) 如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常;
import time, random
from multiprocessing import Process, Queue
def write(q):
print(‘write...%s‘ % (id(q)))
for value in [‘A‘, ‘B‘, ‘C‘]:
print(‘Put %s to queue...‘ % value)
q.put(value)
time.sleep(random.random())
def read(q):
print(‘read...%s,num=%s,flag = %s‘ % (id(q), q.qsize(), q.empty()))
while True:
if not q.empty():
value = q.get()
print(‘Get %s from queue.‘ % value)
time.sleep(3)
if __name__ == ‘__main__‘:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 启动子进程pw,写入:
pw.start()
# 等待pw结束:
# pw.join()
# 启动子进程pr,读取:
pr.start()
# pr.join()
print(‘game over...‘)
执行结果:
game over...
write...2800150905688
Put A to queue...
read...2215658980184,num=1,flag = False
Get A from queue.
Put B to queue...
Put C to queue...
Get B from queue.
Get C from queue.
from multiprocessing import Queue
q = Queue(3)
print(q.empty())
q.put(33)
print(q.empty())
print(q.qsize())
q.put(55)
print(q.full())
print(q.qsize())
q.put(55)
print(q.full())
print(q.qsize())
print(q.get())
print(q.full())
print(q.qsize())
运行结果:
True
False
1
False
2
True
3
33
False
2
如果要使用Pool创建进程, 就需要使用multiprocessing.Manager()中的Queue(), 而不是multiprocessing.Queue(), 否则会抛异常。
import time,random,os
from multiprocessing import Pool,Manager
def reader(q):
print("reader启动(%s),父进程为(%s)"%(os.getpid(),os.getppid()))
for i in range(q.qsize()):
print("reader从Queue获取到消息:%s"%q.get())
def writer(q):
print("writer启动(%s),父进程为(%s)"%(os.getpid(),os.getppid()))
for i in "yongGe":
q.put(i)
if __name__=="__main__":
print("(%s) start"%os.getpid())
q=Manager().Queue() #使用Manager中的Queue来初始化
po=Pool()
# 使用阻塞模式创建进程
# 这样就不需要在reader中使用死循环了,可以让writer完全执行完成后
# 再用reader去读取
po.apply(writer,(q,))
po.apply(reader,(q,))
po.close()
po.join()
print("(%s) End"%os.getpid())
运行结果:
(11484) start
writer启动(10184),父进程为(11484)
reader启动(10256),父进程为(11484)
reader从Queue获取到消息:y
reader从Queue获取到消息:o
reader从Queue获取到消息:n
reader从Queue获取到消息:g
reader从Queue获取到消息:G
reader从Queue获取到消息:e
(11484) End
标签:sel process 数据 默认值 init val 实现类 timeout 操作系统
原文地址:https://www.cnblogs.com/cjr0707/p/9695462.html