Python基础-fork/多进程/Process/进程池/消息队列(Queue)(18)

fork

在unix/linux操作系统中,提供了一个fork()系统函数,它非常特殊。

  • 普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次。因为操作系统自动把当前进程(父进程)复制了一份(子进程),然后分别在父进程和子进程内返回。
  • 子进程永远返回0,父进程返回子进程的ID。
  • 一个父进程可以fork()出很多个子进程。因此,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的id。getpid()可以拿到当前进程id
  • 父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法。
#只能在Linux和unix系统下运行
import os
pid=os.fork()
if pid<0:
    print('fork失败')
elif pid==0: #子进程运行
    print('子进程%s父进程%s'%(os.getpid(),os.getppid()))
else:#父进程运行
    print('父进程%s子进程%s' % (os.getpid(), pid))
print('父子进程都可以执行')

py的多进程创建

由于py是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块提供了一个Process类来代表一个进程对象。

Process类:

构造函数中的参数:
  • target:指定要绑定的函数,也就是进程创建后需要执行的函数
  • name:给创建的进程起一个名字
  • args:给target指定的函数传参,按照位置传参,需要给args传入数租
  • kwargs:需要传入一个字典,给target指定的函数按照键值传参
常用属性:
  • name:当前进程别名,默认为Process-N,N为从1开始递增的整数
  • pid:当前进程实例的PID值。
常用方法:
  • start(): 启动子进程

该句执行时才是正常的创建子进程,而不是Process时创建

  • join([timeout]):回收子进程,等待子进程结束后在继续往下运行,通常用于进程间的同步

父进程阻塞等待子进程执行完毕,timeout设置最长的阻塞时间,如果操作该时间还没有子进程退出,则不在继续等待。

  • p.daemon: 内核帮助应用层记录子进程的退出情况,当使用join函数时,内核会及时返回进程状态给应用层进行处理。
    p.daemon #默认值False,功能让父进程运行结束后,不会影响子进程的运行直到等待子进程运行结束再一起退出。如果设置其值为True,则主进程运行完毕则所有子进程不再运行,一起退出。
    p.daemon 该属性的设置需要在start()前设置。
    该属性的设置并不是将进程设置为linux或unix 中的守护进程。
  • is_alive():判断进程实例是否还在执行。
  • run():如果没有给定target参数,对进程对象调用start()方法时,将执行对象中的run()方法。
  • terminate():不管任务是否完成,立即终止。
from multiprocessing import Process
import os
#子进程执行代码
def run(name):
    print('子进程运行中,name=%s,pid=%d'%(name,os.getpid()))
if __name__=='__main__':
    print('父进程%d'%os.getpid())
		#target为子进程需要执行的函数
    p=Process(target=run,args=('test',))
    print('子进程将执行')
    p.start()
    #等待子进程执行完父进程继续执行
    p.join()
    print('子进程执行结束')
from multiprocessing import Process
import os
#子进程执行代码
def run(name):
    print('子进程运行中,name=%s,pid=%d'%(name,os.getpid()))
if __name__=='__main__':
    print('父进程%d'%os.getpid())
    p=Process(target=run,kwargs={'name':'test',})
    print('子进程将执行')
	#守护父进程,让子进程不在执行了。
    p.daemon=True
    p.start()
    #等待子进程执行完父进程继续执行
    #p.join()
    print('子进程执行结束')

Process子类化

创建新的进程还可以使用类的方式,可以自定义一个类,继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象。我们需要做的就是重写run()方法,这点和java多线程的创建方式很像。

from multiprocessing import Process
import os
import time
class Process_Test(Process):
    def __init__(self,interval):
        Process.__init__(self)
        self.interval=interval
    #重写run方法
    def run(self):
        print('子进程%d,父进程%d'%(os.getpid(),os.getppid()))
        t_start=time.time()
        time.sleep(self.interval)
        t_stop=time.time()
        print('%s执行结束,耗时%0.2f'%(os.getpid(),t_stop-t_start))
if __name__=='__main__':
    p=Process_Test(2)
    p.start()
    p.join()
    print('主进程%s'%os.getpid())
'''
子进程1448,父进程11976
1448执行结束,耗时2.00
主进程11976

'''

进程池

当需要创建的子进程数量不多时,可以利用上面的Process动态生成多个进程,如果创建的进程多,可以使用multiprocessing模块提供的Pool方法。
初始化进程池Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池子没有满,那么就创建一个新的进程来执行该请求。如果池中进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。

常用函数:

  • apply_async(func[,args[,kwds]]):异步,使用非阻塞方式(父进程不阻塞)调用func(并行执行,阻塞方式必须等待上一个进程退出才能执行下一个进程),args为func参数列表,kwds为func的关键字参数列表
  • apply(func[,args[,kwds]]):阻塞方式调用func(父进程阻塞)
  • close():关闭pool,使其不在接受新任务。
  • terminate():不管任务是否完成,立即终止。
  • join():主进程阻塞,等待子进程的退出,必须在close/termanate之后使用。

通过下面例子我们可以看出,当创建出三个进程后,只有其中一个进程执行完毕,才会在创建一个新的进程。

from multiprocessing import Pool
import os,time,random
def worker(msg):
    t_start=time.time()
    print('%s开始执行,进程号%d'%(msg,os.getpid()))
    time.sleep(random.random()*2)
    t_stop=time.time()
    print(msg,'执行完毕,耗时%0.2f'%(t_stop-t_start))
if __name__=='__main__':
    pool = Pool(3)  # 创建一个进程池,最大进程数为3
    for i in range(10):
        # 每次循环将会用空闲出来的子进程去调用目标
        # 异步
        pool.apply_async(worker, (i,))
    print('--start--')
    pool.close()  # 关闭进程池
    pool.join()  # 等待进程池中所有子进程执行完毕,必须放在close()之后
    print('--end--')
'''
#异步执行结果
--start--
0开始执行,进程号9244
1开始执行,进程号12652
2开始执行,进程号732
2 执行完毕,耗时0.26
3开始执行,进程号732
3 执行完毕,耗时0.35
4开始执行,进程号732
0 执行完毕,耗时0.80
5开始执行,进程号9244
1 执行完毕,耗时0.80
6开始执行,进程号12652
4 执行完毕,耗时0.25
7开始执行,进程号732
7 执行完毕,耗时0.40
8开始执行,进程号732
6 执行完毕,耗时1.74
9开始执行,进程号12652
8 执行完毕,耗时1.45
5 执行完毕,耗时1.99
9 执行完毕,耗时0.35
--end--

#同步执行结果
0开始执行,进程号6360
0 执行完毕,耗时0.47
1开始执行,进程号12684
1 执行完毕,耗时1.99
2开始执行,进程号8780
2 执行完毕,耗时1.17
3开始执行,进程号6360
3 执行完毕,耗时0.23
4开始执行,进程号12684
4 执行完毕,耗时1.01
5开始执行,进程号8780
5 执行完毕,耗时1.69
6开始执行,进程号6360
6 执行完毕,耗时0.31
7开始执行,进程号12684
7 执行完毕,耗时0.86
8开始执行,进程号8780
8 执行完毕,耗时0.66
9开始执行,进程号6360
9 执行完毕,耗时1.88
--start--
--end--
'''

消息队列

注意:

进程池使用消息队列的类和Process类使用的消息队列的类是不同的。

使用Process进程方式创建消息队列

from multiprocessing import Process,Pool,Queue
import os,time,random
def write(q):
    for item in 'ABC':
        print('写消息%s'%item)
        q.put(item)
        time.sleep(random.random())
def read(q):
    while True:
        if not q.empty():
            item=q.get()
            print('读消息%s'%item)
            time.sleep(random.random())
        else:
            break
if __name__=='__main__':
    q=Queue()
    pw=Process(target=write,args=(q,))
    pw.start()
    pw.join()
    pr=Process(target=read,args=(q,))
    pr.start()
    pr.join()
'''
写消息A
写消息B
写消息C
读消息A
读消息B
读消息C

'''

使用进程池方式创建消息队列

from multiprocessing import Manager,Pool
import os,time,random
def write(q):
    for item in 'ABC':
        print('写消息%s'%item)
        q.put(item)
        time.sleep(random.random())
def read(q):
    while True:
        if not q.empty():
            item=q.get()
            print('读消息%s'%item)
            time.sleep(random.random())
        else:
            break
if __name__=='__main__':
    #进程池使用消息队列,消息队列只能使用这一种
    q=Manager().Queue()
    pool=Pool(3)
    #创建写进程
    pool.apply(write,(q,))
    #创建读进程
    pool.apply(read,(q,))
    pool.close()
    pool.join()
'''
写消息A
写消息B
写消息C
读消息A
读消息B
读消息C

'''

你可能感兴趣的:(Python基础,队列,python,linux,多线程)