multithreading - 在Python程序中设计并发

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

我正在设计一个大型项目,我想我看到了一种可以通过利用多核来大大提高性能的方法。但是,我对多处理的经验为零,我有点担心我的想法可能不是好主意。

想法

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

动机

使这些函数在自己的进程中同时执行似乎应该很容易。但是查看多处理模块的文档会让我重新考虑-似乎没有任何简单的方法可以在线程之间共享大型数据结构。我忍不住想像这是故意的。

问题

因此,我想我需要知道答案的基本问题是:

  • 是否有实际方法允许多个线程访问相同的列表/字典/等,以便同时进行读取和写入?我是否可以启动我的恒星生成器的多个实例,让它访问包含所有恒星的字典,并从其他线程的角度来看,新对象似乎在字典中突然存在了(也就是说,我不会必须从创建它的过程中明确地夺走星星;我只是将它从dict中拉出来,就好像主线程将它放在那里一样)。
  • 如果没有,是否有任何可行的方法允许多个线程同时读取相同的数据结构,但将它们的结果数据反馈回主线程以安全地转换为相同的数据结构?
  • 即使我确保没有两个并发函数试图同时访问同一数据结构(读取或写入),该设计是否也可以工作?
  • 数据结构可以在所有进程之间固有地共享吗?还是像在TCP流上进行通信的进程那样,我总是总是必须显式地将数据从一个进程发送到另一个进程吗?我知道有些物体可以抽象出这种东西,但是我在问它是否可以完全消除?每个线程正在查看的对象实际上是同一块内存。
  • 模块提供的对象用于抽象化进程之间的通信的灵活性如何?我可以使用它们代替现有代码中使用的数据结构,而不会注意到任何区别吗?如果我做这样的事情,是否会导致大量的开销?

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

    示例

    为了最大程度的清晰起见,下面是一个我想象系统如何工作的示例:

    播放器已指示UI模块将 View 移到特定的空间区域。它将此通知给内容管理模块,并要求它确保玩家当前可以单击的所有星星都已完全生成并且可以被单击。

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

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

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

    然后,UI会注册玩家现在确实点击了星标,并查找需要显示在已点击代表 Sprite 的星标对象上的数据。如果数据在那里,它将显示它;否则,它将显示它。如果不是,则UI会显示一条消息,要求玩家等待,然后继续反复尝试访问星形对象的必要属性,直到成功为止。

    最佳答案

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

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

    import multiprocessing as m
    
    manager = m.Manager()
    starsdict = manager.dict()
    
    process = Process(target=yourfunction, args=(starsdict,))
    process.run()
    

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

    您可以在documentation中阅读有关此内容的更多信息。

    我不知道如果两个进程同时访问它们,代理将如何 react 。由于它们是为并行而设计的,所以我猜它们应该是安全的,即使我听说不是。最好是自己进行测试或在文档中查找。

    关于multithreading - 在Python程序中设计并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23002272/

    相关文章:

    multithreading - 关于我 sleep 的理发师和用餐的哲学家算法正确性的提示(不是答案)?

    c++ - 多线程计算mean和std并不能提高效率

    python - 使用moviepy包(Python)的问题

    concurrency - 招聘经理希望开发人员理解哪些并发编程概念?

    c++ - 并发无序映射查找线程调用的函数是否安全?

    c++ - 多线程应用程序中的日志队列

    java - 在多线程系统中处理POI临时文件

    python - PyQt5 和 Anaconda : ModuleNotFoundError: No module named 'PyQt5'

    python - 将字符串的一部分附加到列表中

    c++ - C++ std::mutex 如何绑定(bind)到资源?