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

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

我发现这很难(也许是因为我没有任何关于它的理论背景)来理解threading.Thread()对象和multiprocessing.Process()之间的区别。

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

我在文档中找到了清楚的例子,但不是很详尽; 只要我尝试让事情变得复杂一些,就会收到很多奇怪的错误(如不能被腌渍的方法等)。

那么,我应该何时使用threadingmultiprocessing模块?

你能把我和一些资源联系起来解释这两个模块背后的概念,以及如何正确使用它们来完成复杂的任务吗?


Giulio Franco所说的对于多线程与一般的多处理来说是真实的。

但是,Python *还有一个额外的问题:有一个全局解释器锁,它可以防止同一进程中的两个线程同时运行Python代码。 这意味着如果您有8个内核,并且将您的代码更改为使用8个线程,则它将无法使用800%CPU并且运行速度提高8倍; 它将使用相同的100%CPU并以相同的速度运行。 (实际上,它会运行得慢一点,因为线程会带来额外的开销,即使你没有任何共享数据,但现在就忽略它。)

这也有例外。 如果你的代码繁重的计算实际上并不是在Python中发生的,但是在一些使用自定义C代码的库中可以正确处理GIL,就像一个numpy应用程序一样,你将从线程中获得预期的性能优势。 如果大量计算是由您运行并等待的某个子进程完成的,则情况也是如此。

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

但是如果你用纯Python写一个CPU绑定的程序,使用更多的线程通常是没有用的。

使用单独的流程对GIL没有这样的问题,因为每个流程都有自己独立的GIL。 当然,在任何其他语言中,线程和进程之间仍然存在所有相同的折衷 - 在进程之间共享数据比线程之间共享数据更困难和更昂贵,运行大量进程或创建和销毁代价可能很高他们经常等等。但是,GIL严重依赖于对流程的平衡,而对于C或Java这种情况并非如此。 因此,您会发现自己在Python中比使用C或Java更频繁地使用多处理。


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

如果你使用独立的“作业”来设计你的代码,除了输入和输出之外,其他作业(或主程序)不共享任何东西,你可以使用concurrent.futures库在线程池中编写代码喜欢这个:

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.Threadmultiprocessing.Process而不是依赖于池。 你将不得不显式创建queue.Queuemultiprocessing.Queue对象。 (还有很多其他选项 - 管道,套接字,带有群集的文件,......但问题是,如果Executor的自动魔法不足,则必须手动执行某些操作。)

但是如果你甚至不能依靠消息传递呢? 如果你需要两项工作来改变相同的结构,并且看到彼此的变化呢? 在这种情况下,您需要手动同步(锁,信号量,条件等),并且如果要使用进程,则需要显式共享内存对象来引导。 这是多线程(或多处理)变得困难的时候。 如果你能避免它,好极了; 如果你不能,你需要阅读的内容比能够放入SO答案的人更多。


从评论中,你想知道Python中的线程和进程有什么不同。 真的,如果你阅读朱利奥佛朗哥的答案和我的所有链接,这应该涵盖一切......但总结肯定会有用,所以这里有:

  • 线程默认共享数据; 过程不。
  • 作为(1)的结果,在进程之间发送数据通常需要酸洗和取消它。**
  • 作为(1)的另一个结果,在进程之间直接共享数据通常需要将其放入像Value,Array和ctypes类型这样的低级格式。
  • 流程不受GIL限制。
  • 在某些平台(主要是Windows)上,创建和销毁过程要昂贵得多。
  • 对进程有一些额外的限制,其中一些在不同平台上有所不同。 详细信息请参阅编程指南。
  • threading模块没有multiprocessing模块的某些功能。 (您可以使用multiprocessing.dummy在线程之上获取大部分缺失的API,或者可以使用诸如concurrent.futures类的更高级别的模块,而不用担心它。)

  • *这实际上并不是Python这个语言,而是CPython,它是该语言的“标准”实现。 其他一些实现没有GIL,就像Jython一样。

    **如果您使用fork启动方法进行多处理 - 您可以在大多数非Windows平台上使用这种方法,则每个子进程都会获取父进程启动时的任何资源,这可以是将数据传递给子进程的另一种方式。


    多个线程可以存在于一个进程中。 属于同一进程的线程共享相同的内存区域(可以读取和写入相同的变量,并且可以相互干扰)。 相反,不同的进程存在于不同的存储区域,并且每个进程都有自己的变量。 为了沟通,流程必须使用其他渠道(文件,管道或套接字)。

    如果你想并行化一个计算,你可能需要多线程,因为你可能希望线程在同一个内存上合作。

    说到性能,线程创建和管理的速度比流程更快(因为操作系统不需要分配全新的虚拟内存区域),并且线程间通信通常比进程间通信更快。 但是线程很难编程。 线程可以互相干扰,并可以写入彼此的内存,但是发生这种情况的方式并不总是很明显(由于几个因素,主要是指令重新排序和内存缓存),所以您将需要同步原语来控制访问到你的变量。


    我相信这个链接以优雅的方式回答你的问题。

    简而言之,如果其中一个子问题需要等待另一个子问题结束,那么多线程是好的(例如,在I / O繁重的操作中); 相反,如果您的子问题可能同时发生,则建议进行多处理。 但是,您不会创建比核心数量更多的进程。

    链接地址: http://www.djcxy.com/p/55203.html

    上一篇: What are the differences between the threading and multiprocessing modules?

    下一篇: How can I explicitly free memory in Python?