多进程学习总结

   Python 解释器有一个全局解释器锁(PIL),致使每一个 Python 进程中最多同时运行一个线程,所以 Python 多线程程序并不能改善程序性能,不能发挥多核系统的优点,能够经过这篇文章了解。可是多进程程序不受此影响, Python 2.6 引入了 multiprocessing 来解决这个问题。这里介绍 multiprocessing 模块下的进程,进程同步,进程间通讯和进程管理四个方面的内容。 这里主要讲解多进程的典型使用,multiprocessing 的 API 几乎是完复制了 threading 的API, 所以只需花少许的时间就能够熟悉 threading 编程了。python

一 起多进程

from multiprocessing import Process, current_process
import time


def func(i):
    time.sleep(2)
    proc = current_process()
    print("proce.name : ", proc.name)  # 输出进程名
    print("proc.pid :", proc.pid)  # 输出进程ID

if __name__ == "__main__":
    for i in range(10):
        p = Process(target=func, args=(i,))
        p.start()
        # p.join()

        # p.join() 加上这一句以后,就是要等这个进程执行完以后才能进入下一个循环,也就是才能执行下一个进程,这样就失去了多进程的意义

二 进程中再起线程

from multiprocessing import Process, current_process
import time
import threading


def foo():
    print("threading id is ", threading.get_ident())  # 获取当前线和的ID


def func(i):
    time.sleep(2)
    proc = current_process()
    print("proce.name : ", proc.name)  # 输出进程名
    print("proc.pid :", proc.pid)  # 输出进程ID
    p = threading.Thread(target=foo, )  # 进程中再起线程 
    p.start()


if __name__ == "__main__":
    for i in range(10):
        p = Process(target=func, args=(i,))
        p.start()

三  利用os模块来查看各个进程ID

from multiprocessing import Process
import os


def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())   #查看父进程ID
    print('process id:', os.getpid())  #查看当前进程ID
    print("\n\n")


def f(name):
    info('called from child process function f ') 
    print('hello', name)

if __name__ == '__main__':
    info('main process line')
    p = Process(target=f, args=('bob',))
    p.start()
    # p.join()

结果以下: 能够看到,主进程的id就是子进程的父ID编程

 

四 进程间的数据传递---queue

from multiprocessing import Process, Queue
import threading


def f(q):
    q.put([42, None, 'hello'])  # 子进程中put 一个值进入Queue


if __name__ == '__main__':
    q = Queue()
    q.put("test123")  # 主进程中put 一个值进入Queue
    p = Process(target=f, args=(q,))
    p.start()
    p.join()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

能够看到,在这个Queue中取出两个值,因此在子进程中给这个Queue传递的值,在主进程中也能够取出来.windows

  和多线程之间数据传递(共享比较)

import threading
import queue

def f():
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = queue.Queue()
    q.put("test123")
    p = threading.Thread(target=f, )
    p.start()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

结果是:多线程

和进程数据传递比较:这里不用传递这个queue,由于线程之间数据是共享的,在多进程中,若是不传递,则在子进程中就会报未定义的错误,app

不能把线程queue做为参数传给子进程

from multiprocessing import Process, Queue
import threading
import queue

def f(q):
    q.put([42, None, 'hello']) 


if __name__ == '__main__':
    q =  queue.Queue()
    q.put("test123")  
    p = Process(target=f,args=(q,))
    p.start()
    print("444", q.get_nowait())
    print("444", q.get_nowait())

要想在进程之间传递数据,只能是进程queue,不能把线程queue做为参数传给子进程.异步

五 进程间的数据传递---Pipe

from multiprocessing import Process, Pipe


def f(conn):
    conn.send([42, None, 'hello from child'])
    print("",conn.recv()) # prints "from main"
    conn.close()

if __name__ == '__main__':
    parent_conn, child_conn = Pipe() #产生两个返回对象,分别表明两头
    p = Process(target=f, args=(child_conn,))
    p.start()
    print("parent",parent_conn.recv())  # prints "[42, None, 'hello from child']"
    parent_conn.send("from main")  
    p.join()

生成一个Pipe 对象后就自动生成两个返回对象,能够理解成两头,async

也有两个方法 send() 发送数据 recv()接收数据.ide

六 进程间的数据传递---manager

from multiprocessing import Process, Manager
import os

def f(dct, lst):
    d[os.getpid()] = os.getppid()
    l.append(os.getpid())
    # print(l)


if __name__ == '__main__':
    with Manager() as manager:
        dct = manager.dict()  # {} #生成一个manager的字典(不是日常的字典),可在多个进程间共享和传递.
        lst = manager.list(range(5))  # 生成一个列表,可在多个进程间共享和传递
        p_list = []
        for i in range(10):
            p = Process(target=f, args=(dct, lst))
            p.start()
            p_list.append(p)
        for res in p_list:  # 等待全部进程执行完闭结果,这里如如不写就会报错,说系统找不到指定文件
            res.join()
        print(dct)
        print(lst)

结果以下:函数

七 进程锁

from multiprocessing import Process, Lock


def f(l, i):
    l.acquire() #加锁
    print('hello world', i)
    l.release()  #解锁


if __name__ == '__main__':
    lock = Lock()
    for num in range(10):
        p=Process(target=f, args=(lock, num))
        p.start()
        # p.join() # 加上这个以后就要当前进程执行完以后才执行下一下进程

结果以下:性能

from multiprocessing import Process, Lock

def f(l, i):
    l.acquire() #加锁
    print('hello world', i)
    l.release()  #解锁


if __name__ == '__main__':
    lock = Lock()
    for num in range(10):
        p=Process(target=f, args=(lock, num))
        p.start()
        p.join() # 加上这个以后就要当前进程执行完以后才执行下一下进程

结果以下

   能够看到,打印的循序是固定的,并且在执行行明显知道,他是上一个进程执行完以后才执行的下一个进程.

   按道理说,各进程之间数据是独立的,各进程之个对同一分内存数据是不共享的,(上面写的那几种数据共享都不是对同一分内存数据共享,只是复制了以后再共享的),不该该加锁啊,但这里各个进程是共享屏幕的,加锁的目的主要是为了防止屏幕输入输出数据出错.在python3中,进程锁的意义不大.

八 进程池

from multiprocessing import Process, Pool, freeze_support
import time
import os


def Foo(i):
    time.sleep(2)
    print("in process", os.getpid())
    return i + 100

def Bar(arg):
    print('-->exec done:', arg, os.getpid())

if __name__ == '__main__':  # 在windows 中必须写在这个里面,否则出错
    # freeze_support()
    pool = Pool(processes=5)  # 容许进程池同时放入5个进程
    print("主进程", os.getpid())
    for i in range(10):
        pool.apply_async(func=Foo, args=(i,), callback=Bar)  # callback=回调函数,是指这个进程执行完以后要执行的函数
        # callback 函数是主进程调用的
        # pool.apply(func=Foo, args=(i,)) #串行
        # pool.apply_async(func=Foo, args=(i,)) #串行
    print('end')
    pool.close()  # 先close() 再join(),
    pool.join()  # 进程池中进程执行完毕后再关闭,若是注释的话,当主程度执行完以后程序就关闭,而不会等各子进程执行完
    # 进程池的另外一种用法,这种方法不能调用callback函数
    groups = [x * 20 for x in range(10)]
    pool = Pool(processes=5)
    pool.map(Foo, groups)

    apply()是串行,也就是说只有等一个进程执行完才执行下一个进程,apply_async() 是并行,会按进程池中的个数一块儿执行,进程池的方法不少,用些方法不经常使用. map_async() 这是一个异常的map方法,能够调用callback()函数,用法和map同样,只是是异步执行.

相关文章
相关标签/搜索