Python 中使用线程池的多线程

问题描述 投票:0回答:3

我将

C++ renderer
翻译为
Python
C++
渲染器使用每个线程渲染图像的一部分。我想在
Python
做同样的事情。然而,与我的单线程代码版本相比,我的多线程代码版本似乎需要很长时间。我是
Python
中的多处理新手,因此想知道下面的代码是否真正实现了我的想法:创建线程池,添加并执行一些任务并等待所有任务完成?

我知道我无法与我的

C++
版本竞争,但我希望至少击败单线程
Python
版本。

多线程代码

from multiprocessing.pool import ThreadPool

pool = ThreadPool(processes=4)
pool.map(run_task(...), range(11))
pool.close()
pool.join()

单线程代码

for i in range(11):
    Task(...)(i)

任务代码

def run_task(...):
    task = Task(...)
    return task.__call__

class Task():
    def __init__(self, ...):
        ...
    def __call__(self, i):
        ...

编辑:我尝试使用

from multiprocessing import Pool
。这似乎阻止了我在
Python
中的
Canopy IDE
终端。当我从 Windows 命令行运行该文件时,我收到:

C:\Users\Matthias\Documents\Courses\Masterproef\pbrt\Tools\Permeability\src>pyth
on renderer.py
Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Users\Matthias\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.
2.2785.win-x86_64\lib\threading.py", line 810, in __bootstrap_inner
    self.run()
  File "C:\Users\Matthias\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.
2.2785.win-x86_64\lib\threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "C:\Users\Matthias\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.
2.2785.win-x86_64\lib\multiprocessing\pool.py", line 342, in _handle_tasks
    put(task)
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin_
_.instancemethod failed

(这也是为什么我通常更喜欢线程而不是进程。所以 GIL 设计决策对我来说并没有多大意义。)

python multithreading
3个回答
7
投票

您应该使用进程池而不是线程池(请参阅第一个示例此处)。

由于 CPython 的 GIL,多线程不应用于 CPU 密集型任务。

也许这个简短的例子会有帮助(我们称之为

example.py
):

from multiprocessing import Pool
import sys

if __name__ == '__main__':

    job_list = [xrange(10000000)]*6

    if 'p' in sys.argv:
        p = Pool(2)
        print("Parallel map")
        print(p.map(sum, job_list))
    else:
        print("Sequential map")
        print(map(sum, job_list))

我的机器有 2 个核心,

example.py p
(并行)版本的速度是串行版本的两倍。如果我们减少要完成的工作量(将十个数字相加而不是一千万),那么顺序版本会获胜,因为在并行版本中创建进程和委派任务会产生不必要的开销。


1
投票

不能保证多线程Python会更快。

先不说使用线程的开销(对于“较大”的程序来说,这种开销通常可以忽略不计),全局解释器锁(GIL)意味着只有一个实际的纯 Python 线程会运行。当一个线程运行时,其他线程必须等待它删除 GIL(例如在打印期间或调用某些非 python 代码期间)。

因此,如果您的线程任务包含释放 GIL 的阻塞调用,那么多线程 Python 是有利的,但一般情况下不能保证。


0
投票

需要注意的是,Linux 和 Windows 等操作系统之间存在差异。在 Linux 中,下一个代码运行良好并利用了并行化:

from multiprocessing.pool import ThreadPool
with ThreadPool(processes=8) as pool:
  result = list(pool.imap(your_func, list_of_items))

但在 Windows 中,它只是打开许多在新进程中调度的线程。因此,按照之前的建议,下一个代码在 Windows 中对我来说效果很好:

 from multiprocessing import Pool
 with Pool(8) as pool:
    result = list(pool.map(your_func, list_of_items))

我使用Python 3.10.7,Windows 11 Pro

© www.soinside.com 2019 - 2024. All rights reserved.