Python的进程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python的进程相关的知识,希望对你有一定的参考价值。

进程

说明:本文是基于Py2.X环境,3.X在我电脑上出了些问题。两者差别并不大。

Python实现多进程的方式主要有两种:一种方法是使用os模块中的fork方法; 另一种是使用multiprocessing模块。这两种方法的区别在于前者仅适用于Unix/Linux操作操作。对win是不支持的,而后者则是跨平台的实现方式。

使用os模块中的fork方式实现多进程。

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:


import os

print ‘Process (%s) start...‘ % os.getpid()

pid = os.fork()

if pid == 0:

    print ‘I am child process (%s) and my parent is %s.‘ % (os.getpid(), os.getppid())

else:

    print ‘I (%s) just created a child process (%s).‘ % (os.getpid(), pid)

得到:

Process (2450) start...

I (2450) just created a child process (2451).

I am child process (2451) and my parent is 2450.

使用Multiprocessing查模块创建多进程。

multiprocessing模块提供了一个Process类来描述一个进程对象,创建子进程时,只需要传入一个执行函数和函数的参数即可完成一个Process实例的创建,用start()方法启动进程,用join()方法实现进程间的同步。join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。


# -*- coding:utf-8 -*-

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 ‘Process will start.‘

    p.start()

    p.join()

    print ‘Process end.‘

得到:

Parent process 2533.

Process will start.

Run child process test (2534)...

Process end.

####multiprocessing模块提供了一个pool类来代表进程池对象

Pool可以提供指定数量的进程供用户调用,默认大小是cpu的核数,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求,但如果池的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束才会创建新的进程来处理它。


# -*- coding:utf-8 -*-

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()

    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.‘

得到: 

Parent process 2541.

Waiting for all subprocesses done...

Run task 0 (2543)...

Run task 1 (2544)...

Run task 2 (2545)...

Run task 3 (2546)...

Task 0 runs 0.02 seconds.

Run task 4 (2543)...

Task 2 runs 0.60 seconds.

Task 4 runs 1.18 seconds.

Task 3 runs 1.26 seconds.

Task 1 runs 1.66 seconds.

All subprocesses done.

对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

进程间的通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。两者的区别在于Pipe常用于两个进程间的通讯而Queue用于多个进程间实现通讯。

Queue通讯

Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传输,有两个方法:put和get进行Queue操作。

  • put方法用以插入数据队列中它可以有两个可选参数:blocked和timeout,如果blocked为True(默认值)并且timeout是正值,该方法会阻塞timeout指定的时间,直到该队列有剩余空间,如果超时,会抛出Queue.Full异常,如果blocked为False,但该Queue已满,则会立即抛出Queue.Full异常。

  • Get方法用以从队列读取并且删除一个元素。它可以有两个可选参数:blocked和timeout,如果blocked为True(默认值)并且timeout是正值,那么在等待时间内没有取到任何元素会抛出Queue.Empty异常,如果blocked为False,分两种情况:如果Queue有一个值木口月禾,则立即返回该值,否则如果队列为空,则立即抛出Queuq.Empty异常。

# -*- coding:utf-8 -*-

from multiprocessing import Process, Queue

import os, time, random

# 写数据进程执行的代码:

def write(q):

    for value in [‘A‘, ‘B‘, ‘C‘]:

        print ‘Put %s to queue...‘ % value

        q.put(value)

        time.sleep(random.random())

# 读数据进程执行的代码:

def read(q):

    while True:

        value = q.get(True)

        print ‘Get %s from queue.‘ % value

if __name__ == ‘__main__‘:

    # 父进程创建Queue,并传给各个子进程:

    q = Queue()

    pw = Process(target=write, args=(q,))

    pr = Process(target=read, args=(q,))

    # 启动子进程pw,写入:

    pw.start()

    # 启动子进程pr,读取:

    pr.start()

    # 等待pw结束:

    pw.join()

    # pr进程里是死循环,无法等待其结束,只能强行终止:

    pr.terminate()

得到:

Put A to queue...

Get A from queue.

Put B to queue...

Get B from queue.

Put C to queue...

Get C from queue.
Pipes通讯

Pipe常用来在两个进程间进行通信,两个进程分别位于管道的两端。

Pipe方法返回(conn1,conn2)代表一个管道的两个端,Pipe方法有duplex参数,如果duplex参数为True(默认值),那么这个管道是全双工模式,也就是说conn1和conn2均可收发,若duplex为False,conn1只负责接收消息,conn2只负责发送消息。send和recv方法分别是发送和接收消息的方法。例如,在全双工模式下,可以调用conn1.send发送消息,conn1.recv接收消息。如果没有消息可接收,recv方法会一直阻塞。如果管道已经被关闭,那么recv方法会抛出EOFError.


import multiprocessing

import random

import time, os

def proc_send(pipe, urls):

    for url in urls:

        print "process(%s) send:%s" % (os.getpid(), url)

        pipe.send(url)

        time.sleep(random.random())

def proc_recv(pipe):

    while True:

        print "Process(%s) rev:%s" % (os.getpid(), pipe.recv())

        time.sleep(random.random())

if __name__ == "__main__":

    pipe = multiprocessing.Pipe()

    p1 = multiprocessing.Process(target=proc_send,args=(pipe[0],[‘url_‘+str(i) for i in range(10)]))

    p2 = multiprocessing.Process(target=proc_recv,args=(pipe[1],))

    p1.start()

    p2.start()

    p1.join()

    p2.join()

得到:

process(1134) send:url_0

Process(1135) rev:url_0

process(1134) send:url_1

Process(1135) rev:url_1

process(1134) send:url_2

Process(1135) rev:url_2

process(1134) send:url_3

Process(1135) rev:url_3

process(1134) send:url_4

Process(1135) rev:url_4

process(1134) send:url_5

Process(1135) rev:url_5

process(1134) send:url_6

Process(1135) rev:url_6

process(1134) send:url_7

Process(1135) rev:url_7

process(1134) send:url_8

Process(1135) rev:url_8

process(1134) send:url_9

Process(1135) rev:url_9

以上是关于Python的进程的主要内容,如果未能解决你的问题,请参考以下文章

[Python3] 043 多线程 简介

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

代码片段:Shell脚本实现重复执行和多进程

python中的多线程和多进程编程

使用 asyncio 将 bash 作为 Python 的子进程运行,但 bash 提示被延迟

LINUX PID 1和SYSTEMD PID 0 是内核的一部分,主要用于内进换页,内核初始化的最后一步就是启动 init 进程。这个进程是系统的第一个进程,PID 为 1,又叫超级进程(代码片段