跳至主要內容

多进程

yczha大约 3 分钟python基础python语法python

Python基础系列内容为学习廖雪峰老师Python3教程的记录,廖雪峰老师官网地址:廖雪峰Python3教程open in new window

  • 基本解释:对于操作系统来说,一个任务就是一个进程Process,有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程Thread

  • 执行多任务:Python内执行多任务的方式有三种,即:多线程多进程多进程+多线程

  • 多进程multiprocessing:Python实现多进程在Linux 等系统上提供系统调用fork() 函数,而在Windows 系统上提供了Process 来提供多进程使用

    from multiprocessing import Process
    import os
    
    # 子进程要执行的代码
    def run_proc(name):
        print('Run child process %s (%s)...' % (name, os.getpid()))
    
    if __name__=='__main__':
        print('Parent process %s.' % os.getpid())
        p = Process(target=run_proc, args=('test',))
        print('Child process will start.')
        p.start()
        p.join()
        print('Child process end.')
    

    运行上述代码:

    Parent process 8072.
    Child process will start.
    Run child process test (14316)...
    Child process end.
    [Finished in 0.6s]
    

    如果要启动大量进程同时执行,可以使用Python提供的进程池:

    from multiprocessing import Pool
    import os, time, random
    
    def long_time_task(name):
        print('Run task %s (%s)...' % (name, os.getpid()))
        start = time.time()
        time.sleep(random.random() * 3)
        end = time.time()
        print('Task %s runs %0.2f seconds.' % (name, (end - start)))
    
    if __name__=='__main__':
        print('Parent process %s.' % os.getpid())
        p = Pool(4)
        for i in range(5):
            p.apply_async(long_time_task, args=(i,))
        print('Waiting for all subprocesses done...')
        p.close()
        p.join()
        print('All subprocesses done.')
    
  • 子进程

    很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。

    subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。

    下面的代码演示了调用命令nslookup www.python.org 的结果,这跟在命令行直接输入该命令效果是一样:

    import subprocess
    print('nslookup www.python.org')
    r = subprocess.call(['nslookup', 'www.python.org'])
    print('Exit code:', r)
    

    结果如下:

    nslookup www.python.org
    Non-authoritative answer:
    
    Server:  localhost
    Address:  127.0.0.1
    
    Name:    dualstack.python.map.fastly.net
    Address:  151.101.72.223
    Aliases:  www.python.org
    
    Exit code: 0
    

    如果子进程还需要输入,则可以通过communicate() 方法来输入:

    import subprocess
    
    print('nslookup')
    p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
    print(output.decode('utf-8'))
    print('Exit code:', p.returncode)
    

    结果:

    nslookup
    Default Server:  localhost
    Address:  127.0.0.1
    > > Server:  localhost
    Address:  127.0.0.1
    > 
    Exit code: 0
    
  • 进程间通信

    Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了QueuePipes等多种方式来交换数据。

    我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:

    from multiprocessing import Process,Queue
    import os,time,random
    
    # 写数据
    def write(q):
        print("Process to write:%s"%os.getpid())
        for value in ['A','B','C']:
            print('Put %s to queue...'%value)
            q.put(value)
            time.sleep(random.random())
    # 读数据
    def read(q):
        print("Process to read: %s"%os.getpid())
        while True:
            value=q.get(True)
            print('Get %s from queue.'%value)
    
    if __name__=="__main__":
        q=Queue()
        pw=Process(target=write,args=(q,))
        pr=Process(target=read,args=(q,))
        pw.start()
        pr.start()
        pw.join()
        pr.terminate()
    

    结果:

    Process to read: 16104
    Process to write:18016
    Put A to queue...
    Get A from queue.
    Put B to queue...
    Get B from queue.
    Put C to queue...
    Get C from queue.