线程和多处理模块之间有什么区别?

2024-03-19

我正在学习如何使用threadingmultiprocessingPython 中的模块可并行运行某些操作并加速我的代码。

我发现这很难(也许是因为我没有任何理论背景)来理解threading.Thread()对象和一个multiprocessing.Process() one.

另外,我并不完全清楚如何实例化一个作业队列,并让其中只有 4 个(例如)作业并行运行,而另一个则在执行之前等待资源释放。

我发现文档中的示例很清楚,但不是很详尽;一旦我尝试使事情变得复杂一点,我就会收到很多奇怪的错误(例如无法腌制的方法等等)。

那么,我什么时候应该使用threading and multiprocessing模块?

您能给我链接一些资源来解释这两个模块背后的概念以及如何正确使用它们来完成复杂的任务吗?


朱利奥·佛朗哥怎么说 https://stackoverflow.com/a/18114475对于多线程与多处理来说是正确的一般来说.

However, Python* has an added issue: There's a Global Interpreter Lock that prevents two threads in the same process from running Python code at the same time. This means that if you have 8 cores, and change your code to use 8 threads, it won't be able to use 800% CPU and run 8x faster; it'll use the same 100% CPU and run at the same speed. (In reality, it'll run a little slower, because there's extra overhead from threading, even if you don't have any shared data, but ignore that for now.)

但也有例外。如果您的代码的繁重计算实际上并未发生在 Python 中,而是在某些具有执行正确 GIL 处理的自定义 C 代码的库中(例如 numpy 应用程序),您将从线程中获得预期的性能优势。如果繁重的计算是由您运行并等待的某个子进程完成的,情况也是如此。

更重要的是,在某些情况下这并不重要。例如,网络服务器花费大部分时间从网络读取数据包,而 GUI 应用程序花费大部分时间等待用户事件。在网络服务器或 GUI 应用程序中使用线程的原因之一是允许您执行长时间运行的“后台任务”,而无需停止主线程继续服务网络数据包或 GUI 事件。这对于 Python 线程来说效果很好。 (用技术术语来说,这意味着 Python 线程为您提供并发性,即使它们不为您提供核心并行性。)

但是,如果您用纯 Python 编写 CPU 密集型程序,那么使用更多线程通常没有帮助。

使用单独的进程就没有GIL这样的问题,因为每个进程都有自己单独的GIL。当然,与任何其他语言一样,您仍然需要在线程和进程之间进行权衡 - 在进程之间共享数据比在线程之间共享数据更困难且更昂贵,运行大量进程或创建和销毁进程的成本可能会很高但 GIL 严重影响了进程的平衡,而对于 C 或 Java 来说,情况并非如此。因此,您会发现自己在 Python 中比在 C 或 Java 中更频繁地使用多处理。


与此同时,Python 的“自带电池”哲学带来了一些好消息:编写可以通过一行更改在线程和进程之间来回切换的代码非常容易。

如果您根据独立的“作业”设计代码,除了输入和输出之外,不与其他作业(或主程序)共享任何内容,则可以使用concurrent.futures http://docs.python.org/3/library/concurrent.futures.html围绕线程池编写代码的库,如下所示:

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    executor.submit(job, argument)
    executor.map(some_function, collection_of_independent_things)
    # ...

您甚至可以获取这些作业的结果并将其传递给进一步的作业,按执行顺序或完成顺序等待事物等;阅读有关的部分Future对象以获取详细信息。

现在,如果事实证明您的程序不断使用 100% CPU,并且添加更多线程只会使其速度变慢,那么您就会遇到 GIL 问题,因此您需要切换到进程。您所要做的就是更改第一行:

with concurrent.futures.ProcessPoolExecutor(max_workers=4) as executor:

唯一真正需要注意的是,作业的参数和返回值必须是可腌制的(并且不会花费太多时间或内存来腌制)才能跨进程使用。通常这不是问题,但有时却是问题。


但如果您的工作不能独立怎么办?如果您可以根据工作来设计代码传递消息从一个到另一个,仍然很容易。你可能需要使用threading.Thread or multiprocessing.Process而不是依赖池。你必须创建queue.Queue or multiprocessing.Queue明确对象。 (还有很多其他选项——管道、套接字、带有羊群的文件……但重点是,你必须这样做某物如果执行器的自动魔法不够,则手动进行。)

但是如果您甚至不能依赖消息传递怎么办?如果您需要两项工作来改变相同的结构并查看彼此的变化怎么办?在这种情况下,您将需要进行手动同步(锁、信号量、条件等),并且如果您想使用进程,还需要显式共享内存对象来启动。这是多线程(或多处理)变得困难的时候。如果你能避免它,那就太好了;如果你不能,你将需要阅读比某人可以放入 SO 答案更多的内容。


从评论中,您想了解 Python 中线程和进程之间的区别。真的,如果您阅读朱利奥·佛朗哥的答案和我的答案以及我们所有的链接,那应该涵盖所有内容……但是摘要肯定会很有用,所以这里是:

  1. 线程默认共享数据;进程则不然。
  2. As a consequence of (1), sending data between processes generally requires pickling and unpickling it.**
  3. 作为(1)的另一个结果,在进程之间直接共享数据通常需要将其放入低级格式,如值、数组和ctypes types.
  4. 进程不受 GIL 的约束。
  5. 在某些平台(主要是 Windows)上,创建和销毁进程的成本要高得多。
  6. 进程有一些额外的限制,其中一些在不同平台上是不同的。看编程指南 http://docs.python.org/3/library/multiprocessing.html#multiprocessing-programming了解详情。
  7. The threading模块不具备某些功能multiprocessing模块。 (您可以使用multiprocessing.dummy要在线程之上获取大部分缺失的 API,或者您可以使用更高级别的模块,例如concurrent.futures不用担心。)

* It's not actually Python, the language, that has this issue, but CPython, the "standard" implementation of that language. Some other implementations don't have a GIL, like Jython.

** If you're using the fork https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods start method for multiprocessing—which you can on most non-Windows platforms—each child process gets any resources the parent had when the child was started, which can be another way to pass data to children.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程和多处理模块之间有什么区别? 的相关文章

随机推荐