1

我有一个数字列表。我想对列表中的每个数字执行一些耗时的操作,并创建一个包含所有结果的新列表。下面是我有什么简化版本:在Python中使用线程/多处理来同时进行多个计算

def calcNum(n):#some arbitrary, time-consuming calculation on a number 
    m = n 
    for i in range(5000000): 
    m += i%25 
    if m > n*n: 
     m /= 2 
    return m 

nums = [12,25,76,38,8,2,5] 
finList = [] 

for i in nums: 
    return_val = calcNum(i) 
    finList.append(return_val) 

print(finList) 

现在,我想利用我的CPU中的多个核心,并给予他们每个人处理的数字之一的任务,而且由于“数字计算“功能是从头到尾自成一体的,我认为这将是相当简单的做法和多处理/线程的完美情况。

我的问题是,我应该使用哪一个(多处理或线程?),以及最简单的方法是什么?

我做了各种代码的测试我在其他问题来实现这一发现,而它运行正常它似乎并没有做任何实际的多线程/处理,只是只要需要我的第一个测试:

from multiprocessing.pool import ThreadPool 

def calcNum(n):#some arbitrary, time-consuming calculation on a number 
    m = n 
    for i in range(5000000): 
    m += i%25 
    if m > n*n: 
     m /= 2 
    return m 

pool = ThreadPool(processes=3) 

nums = [12,25,76,38,8,2,5] 
finList = [] 

for i in nums: 
    async_result = pool.apply_async(calcNum, (i,)) 
    return_val = async_result.get() 
    finList.append(return_val) 

print(finList) 
+0

谢谢!正是我在找什么。现在,如果我想将多个参数传递给池中的每个工作者(例如,如果calcNum(n)现在是calcNum(n,x,y)),我该如何去做这件事? – user3209118

回答

3

multiprocessing.poolpool.map在这里你最好的朋友。由于它隐藏了所有其他复杂的队列以及无法使其工作的所需,因此可以节省大量头痛。您只需设置池,为其分配最大进程数,将其指向该函数并进行迭代。请参阅以下工作代码。

由于join和使用案例pool.map的目的是工作,程序将等待,直到所有进程都返回一些东西,然后再给出结果。

from multiprocessing.pool import Pool 

def calcNum(n):#some arbitrary, time-consuming calculation on a number 
    print "Calcs Started on ", n 
    m = n 
    for i in range(5000000): 
    m += i%25 
    if m > n*n: 
     m /= 2 
    return m 

if __name__ == "__main__": 
    p = Pool(processes=3) 

    nums = [12,25,76,38,8,2,5] 
    finList = [] 


    result = p.map(calcNum, nums) 
    p.close() 
    p.join() 

    print result 

,将让你这样的事情:

Calcs Started on 12 
Calcs Started on 25 
Calcs Started on 76 
Calcs Started on 38 
Calcs Started on 8 
Calcs Started on 2 
Calcs Started on 5 
[72, 562, 5123, 1270, 43, 23, 23] 

无论何时每个进程启动或完成时,地图等待每完成,然后把他们以正确的顺序都回来了(对应于可迭代的输入)。

正如@Guy提到的那样,GIL在这里伤害了我们。您可以在上面的代码中将Pool更改为ThreadPool,并查看它如何影响计算的时间。由于使用了相同的功能,因此GIL一次只允许一个线程使用calcNum函数。所以它足够接近仍然连续运行。 Multirocessingprocesspool本质上启动您的脚本的进一步实例,它绕过GIL的问题。如果您在上述过程中观察正在运行的进程,则会在池运行时看到额外的“python.exe”实例。在这种情况下,您总共会看到4.

+0

一个小小的澄清:'Pool.map'已经等待工人到达结果。人们可以立即“打印结果”,并可能在“关闭”和“连接”之前将更多工作交给池。 –

0

我猜你被蟒蛇影响Global Interpreter Lock

的GIL是有争议的,因为它可以防止多线程程序的CPython在某些情况下,采取多处理器系统的充分利用。

尝试使用multiprocessing代替

from multiprocessing import Pool