Python 多进程 - 就是搞不懂

6 投票
1 回答
4124 浏览
提问于 2025-04-16 19:37

我花了一些时间想弄明白多进程的概念,但有些细节对我这个新手来说还是很难理解。我能让一个进程池返回一个简单的整数,但如果函数的返回结果不是那么简单,比如我在网上找到的例子(甚至在官方文档里),那些例子都让我觉得很晦涩。

这里有一个我想让它正常工作的例子。但是,我就是无法让它按预期工作,我相信一定有简单的原因。我可能需要使用队列、共享内存或者管理器,但我看了很多遍文档,还是搞不清楚这些到底是什么意思,具体是干什么的。到目前为止,我唯一能理解的就是进程池的功能。

另外,我在使用一个类,因为我需要避免使用全局变量,就像在这个问题的回答里提到的那样。

import random

class thisClass:
    def __init__(self):
        self.i = 0

def countSixes(myClassObject):
    newNum = random.randrange(0,10)
    #print(newNum) #this proves the function is being run if enabled
    if newNum == 6:
        myClassObject.i += 1

if __name__ == '__main__':
    import multiprocessing
    pool = multiprocessing.Pool(1) #use one core for now

    counter = thisClass()

    myList = []
    [myList.append(x) for x in range(1000)]

    #it must be (args,) instead of just i, apparently
    async_results = [pool.apply_async(countSixes, (counter,)) for i in myList]

    for x in async_results:
        x.get(timeout=1)

    print(counter.i)

有没有人能用简单易懂的方式解释一下我需要做什么,这样我才能明白我缺少了什么,以及这些东西是干什么的?

1 个回答

12

我花了一些时间才明白你想要实现的效果。问题出在多进程的工作方式上。简单来说,你需要用一种函数式的编程风格来写你的程序,而不是像现在这样依赖副作用。

现在,你把对象发送到池子里去修改,但从 countSixes 函数里什么也没返回。这在多进程中是行不通的,因为为了绕过 全局解释器锁,多进程会创建一个 副本counter,并把它发送到一个 全新的解释器。所以当你增加 i 的值时,实际上是在增加 i 的一个 副本,然后因为你什么也不返回,所以这个副本就被丢弃了!

要实现一些有用的功能,你必须从 countSixes 返回一些东西。这里有一个简化版的代码,做的事情和你想要的类似。我保留了一个参数,只是为了展示你应该怎么做,但其实这个函数可以不带参数。

import random

def countSixes(start):
    newNum = random.randrange(0,10)
    if newNum == 6:
        return start + 1
    else:
        return start

if __name__ == '__main__':
    import multiprocessing
    pool = multiprocessing.Pool(1) #use one core for now

    start = 0
    async_results = [pool.apply_async(countSixes, (start,)) for i in range(1000)]

    print(sum(r.get() for r in async_results))

撰写回答