在Python程序中设计并发性

时间:2014-04-11 02:06:50

标签: multithreading python-3.x concurrency python-multithreading

我正在设计一个大型项目,我认为我看到了一种可以通过利用多核来大幅提升性能的方法。但是,我对多处理没有任何经验,而且我有点担心我的想法可能不是好的。

该程序是一款程序性地生成大量内容的视频游戏。由于一次性生成所有内容太多,程序会尝试在需要之前或之前生成所需内容,并花费大量精力来预测在不久的将来需要什么。那个未来有多接近。因此,整个程序是围绕一个任务调度程序构建的,该任务调度程序通过附加了元数据位的函数对象来帮助确定它们应该处理的顺序并按顺序调用它们。

动机

似乎应该很容易使这些函数在它们自己的进程中并发执行。但是查看多处理模块的文档让我重新考虑 - 似乎没有任何简单的方法可以在线程之间共享大型数据结构。我无能为力,但想象这是故意的。

问题

所以我认为我需要知道答案的基本问题是:

  1. 是否有任何实用的方法允许多个线程同时访问相同的list / dict / etc ...进行读写操作?我可以启动我的星形生成器的多个实例,让它访问包含所有星星的dict,并且从其他线程的角度来看,新的对象看起来只是在dict中存在(也就是说,我不会&#39 ; t必须从制作它的过程中明确地抓住星星;我只是将它从dict中拉出来,好像主线程已经把它放在那里一样。)

  2. 如果没有,是否有任何实用的方法允许多个线程同时读取相同的数据结构,但是将它们的结果数据反馈回主线程以安全地转换到相同的数据结构? / p>

  3. 即使我确保没有两个并发函数同时尝试访问相同的数据结构,无论是阅读还是写作,这个设计都会有效吗?

  4. 数据结构本身是否可以在进程之间共享,或者我是否总是明确地必须将数据从一个进程发送到另一个进程,就像通过TCP流进行通信一样?我知道有些东西可以抽象出那种东西,但是我在问它是否可以完全消除;让每个线程看到的对象实际上都是同一块内存。

  5. 模块提供的对象有多灵活,以抽象出进程之间的通信?我可以将它们用作现有代码中使用的数据结构的替代品,而不会发现任何差异吗?如果我做了这样的事情,会不会造成无法控制的开销?

  6. 对不起我的天真,但我没有正式的计算机科学教育(至少,还没有),我以前从未使用过并发系统。我试图在这里实现甚至远程实用的想法,还是任何允许我同时透明地执行任意功能的解决方案会导致如此多的开销,以至于我最好不要在一个线程中执行所有操作?

    示例

    为了最清晰,这里是我想象系统如何工作的一个例子:

    玩家已指示UI模块将视图移动到特定的空间区域。它通知内容管理模块,并要求它确保玩家当前可以点击的所有星星都已完全生成并准备点击。

    内容管理模块检查并看到UI正在说明玩家可能尝试与之交互的几个明星实际上还没有生成点击时显示的详细信息。它产生许多Task对象,包含那些星的方法,当被调用时,它们将生成必要的数据。它还为这些任务对象添加了一些元数据,假设(可能基于从UI模块收集的更多信息)在播放器尝试点击任何内容之前0.1秒,并且图标最接近光标的星星具有最大值有机会被点击,因此应该比光标远离星星稍早一点请求。然后,它将这些对象添加到调度程序队列。

    调度程序会根据需要完成每个任务的时间快速对其队列进行排序,然后将第一个任务对象从队列中弹出,从其包含的函数中创建一个新进程,然后不再考虑该进程,而只是从队列中弹出另一个任务并将其填入一个进程,然后是下一个,然后是下一个......

    同时,新进程执行,将它生成的数据存储在星形对象上,它是一种方法,并在到达return语句时终止。

    用户界面然后注册玩家现在确实点击了一个星,并查找需要在其代表精灵被点击的星形对象上显示的数据。如果有数据,则显示它;如果不是,UI会显示一条消息,要求玩家等待并继续反复尝试访问星形对象的必要属性,直到成功为止。

1 个答案:

答案 0 :(得分:1)

即使您的问题看起来非常复杂,但仍有一个非常简单的解决方案。您可以隐藏使用代理跨进程共享对象的所有复杂内容。

基本思想是创建一个管理器来管理应该跨进程共享的所有对象。然后,该管理器创建自己的进程,等待其他进程指示它更改对象。但足够说。它看起来像这样:

import multiprocessing as m

manager = m.Manager()
starsdict = manager.dict()

process = Process(target=yourfunction, args=(starsdict,))
process.run()

starsdict中存储的对象不是真正的dict。相反,它会将所有更改和请求发送给其经理。这被称为" proxy",它与它模仿的对象具有几乎完全相同的API。这些代理是pickleable,因此您可以将参数传递给新进程中的函数(如上所示)或通过队列发送它们。

您可以在documentation

中详细了解相关信息

我不知道如果两个进程同时访问它们,代理会如何反应。因为他们是为并行而做的,我猜他们应该是安全的,即使我听说他们不是。如果您自己测试或在文档中查找它,那将是最好的。