这可能会被标记为重复或可能不相关。但我实际上相信这个问题对我和未来缺乏经验的 Python 开发人员都很重要。
由于 GIL,用于 CPU 密集型任务的本地工作队列的概念在 Python 中至关重要。这方面SE上有明显的答案。使用子进程的方法来绕过缺乏真正的CPU有限并行性的问题。在Python中我们可以使用
multiprocessing.Pool
类来实现这一点。我想构建一个
通用工作队列单例
,它可以接受任何函数和任何类型的参数,并在不同的CPU上沿着我的主进程异步处理它,就像工作队列应该的那样。
就是这样很简单。
只是事实并非如此。
问题的基础在于 Pickle 问题。 据我发现,泡菜是有限的。请参阅 这个问题 因为一次可以有类似以下的代码:
def NetworkClassType(topologyClass):
class Network(topologyClass):
...
Pickle 不是通用工作队列的选项。所以我发现我可以使用
dill
然而,我不断遇到一系列必须解决的问题。
从
这个
问题开始。我必须使用此设置,因为我的目标是通用 WQ,有时事情并不在子流程期望的上下文中。
然后我遇到了一个问题,其中来自 pickle 包的代码:
if reduce is not None:
rv = reduce(self.proto)
返回异常
TypeError: 'NoneType' object is not callable
,找出原因是一场噩梦。事实证明,当您将以下代码放入 pickled 类(即使是在 dill 中)时,以下代码会破坏
if
语句:
def __getattr__(self, prop):
return None
这基本上意味着您在尝试获取不存在的属性时不希望出现任何异常在类的
__dict__
中定义。事实证明,pickle 和 dill 无法处理此代码。已经有了通用的 WQ 想法。
但是,好吧,让我们通过从类中删除上面的代码来摆脱这种奇怪的情况。
然后我遇到了 这个错误 ,其中有多个关于如何处理的建议来解决,没有一个真正适合我的情况。
此时,我停了下来。我心想,“我在这里做错了。Python 是一种面向数据的语言,成熟,具有完善的回显系统。通用的工作队列不可能这么复杂。一定有人在我之前做到了这一点。”|| |所以为了明确起见,我在这里自责。我一定错过了什么。 python 中有一个众所周知的 WQ 包吗?也许多处理不是答案?也许莳萝不是答案?也许有一种方法可以绕过 GIL(无需
更改解释器 )?也许我在 SE 的某个地方错过了对所有这些问题的一个简洁答案? 现在我可以继续这样做,我会的。一次解决一个问题,直到弄清楚如何实现我的目标。但如果有人告诉我我错了,我会很高兴。事实上,Python 中有一个工作正常的通用工作队列(或者更好,一个
Actor 队列 ),它可以处理所有事情并且效率很高。 我只能希望。
谢谢.
我仍在从事该项目。 我尝试了上述所有链接(然后是一些链接),并将尝试了解如何使我的 WQ 包工作。
编辑:我忘记解决可能出现的一个明显问题,即操作系统的问题。显然,即使是通用的 WQ 在不同的操作系统上也可能会失败。我的目标是 Windows 10 及以上版本。
EDIT: I forgot to address an obvious question that might arise and that is of the OS. Obviously, even a generic WQ might fail on different OS. Mine is aimed for windows 10 and above.
你对 Python 中工作队列的深入研究,以及你遇到的“pickle 问题”表明你对这个问题有很好的理解。你问对了所有的问题,并且像你一样,很多开发者在寻找一个“通用的”工作队列解决方案时都感到很沮丧。
让我们来分解一下问题,并探索一下可用的选项:
你所面临的挑战
-
Pickle 的限制 : 你说得对,
pickle
(以及一定程度上的dill
)在处理复杂的对象,例如嵌套函数、lambda 或者带有__getattr__
之类魔法方法的类时,会有局限性。这些限制源于 Python 代码执行的动态本质,使其难以在进程之间序列化所有内容。 -
通用的痛点 : 创建一个真正通用的工作队列,可以处理任何函数或数据结构,在 Python 中,或者说在任何语言中,都是一项艰巨的任务。总是有可能出现边缘情况,或者依赖于特定库或配置的代码,而这些代码不能轻易地序列化或反序列化。
可选方案
-
拥抱限制
-
简化你的函数 : 如果你能将你的工作函数设计成独立的、可以被
pickle
轻松序列化的函数,那么multiprocessing.Pool
就能很好地工作。这通常涉及到将任何复杂的状态或依赖项传递到函数参数中(确保这些参数也是可序列化的)。 -
自定义序列化 : 对于棘手的对象,你可以探究
pickle
的__reduce__
或dill
的扩展机制,以实现自定义的序列化和反序列化逻辑。这需要一些工作量,但可以提供更大的灵活性。 -
探索其他工作队列库
-
Celery : 虽然通常用于分布式任务队列,但 Celery 也可以用于本地多处理。它提供了强大的功能,例如任务调度、重试以及与各种消息代理的集成。然而,它也带有更大的复杂性,可能需要更多的设置。
- RQ (Redis Queue) : RQ 是一个更简单的任务队列,它使用 Redis 作为消息代理。它易于使用,并且可以很好地处理通用函数,但它依赖于 Redis 服务器。
-
Gearman : Gearman 是一个通用的分布式工作队列系统,支持多种语言,包括 Python。它可以处理比简单的进程间通信更复杂的工作流程。
-
考虑替代架构
-
异步编程 : 对于 I/O 密集型任务,异步编程(使用
asyncio
库)可以是更有效率的解决方案。它允许在单个线程中并发执行代码,从而避免了 GIL 问题。 - 微服务 : 对于更大型或更复杂的系统,你可以考虑将你的应用程序分解成更小的、独立的微服务,这些微服务通过网络进行通信。这种方法提供了更大的灵活性,并且可以利用不同的技术和语言。
没有银弹
选择最佳方案取决于你的具体需求和限制。
-
简单性与功能
:
multiprocessing.Pool
易于使用,但功能有限。Celery 和其他工作队列提供了更多的功能,但增加了复杂性。 - 性能 : 进程间通信的开销很大,因此,如果性能至关重要,那么你可能需要探索其他架构,例如异步编程或微服务。
- 可维护性 : 选择一个对你和你的团队来说易于理解和维护的解决方案非常重要。
最后的想法
你对 Python 工作队列的深入研究值得赞赏。不要气馁,因为没有找到通用的解决方案。专注于你的特定需求,并选择最适合你的用例的方案。记住,即使是最复杂的系统通常也是从更简单的构建块开始构建的。
标签:python,worker,dill From: 78787203