在python中同步两个共享对象的读/写操作

Posted

技术标签:

【中文标题】在python中同步两个共享对象的读/写操作【英文标题】:syncing read/write operations for two shared objects in python 【发布时间】:2021-12-21 22:40:55 【问题描述】:

假设我使用multiprocessing.Manager 创建两个共享对象并将它们传递给子进程,该子进程开始写入这些对象:

manager = multiprocessing.Manager()
a = manager.list()
b = manager.list()
subprocess = MyProcess(a,b)
subprocess.start()  # writing to a and b starts here

# inspect a and b - in what order will changes appear?

有没有办法确保这些写入反映在父进程中的顺序与写入在子进程中执行的顺序相同?如果我register() 一个有两个成员的类,而子流程对这两个成员进行了更改怎么办?

有没有更一般地回答这些“操作顺序”问题的参考资料? (我在文档中找不到这个)。例如:如果我们生成第二个子进程subprocess_2,它也会在ab 上进行一些写入和读取——我们能说的关于顺序更改的内容将反映在父进程和subprocess_2 中?

简单、详细的示例:按照 Booboo 的示例,如果 MyProcess(multiprocessing.Process 的子类)像这样实现 run()

def run(self) :
    a.append(1)
    b.append(2)

然后如果我们等待足够长的时间,我们知道父进程将看到a == [1]b == [2]。问题是我们可以在两者之间看到哪些可能的状态。如果管理器中有某种​​全局同步,我们将只能看到a,b 的以下值对:[],[][1],[] 或最终状态[1],[2]。但是如果没有这样的同步,我们也许可以瞥见[],[2](例如,如果关于追加到b 的消息更快地到达父级,或者队列的轮询顺序不是我们所期望的(什么我们期待吗?))。我希望不必查看源代码(在未来的版本中也可能会更改),而是希望得到一个一般保证,如果有的话。希望这可以澄清问题。

【问题讨论】:

我理解可能不是最好的答案,但多线程,尤其是同步,线程之间共享数据并不是 Python 最强大的方面。您是否为此目的选择了正确的工具? 我的应用程序中的主要计算工作是在 Java 中完成的,我想这是我已经习惯于获得关于操作顺序的精确保证的地方(由“java 内存模型”提供)。 python 包装器正在做一些非常基本的事情。在 python 中,我只是将并发用作在设计中获得更多灵活性的一种方式。 【参考方案1】:

在您展示的示例中,您正在处理一个 托管 列表。此列表存在于您执行manager = multiprocessing.Manager() 时创建的进程中。变量ab 实际上是proxy 对象。当在这些代理上调用方法时,从一个进程的地址空间到 SynchManager 的地址空间(multiprocessing.SyncManager 是被创建的类)执行远程方法调用(通信机制是 Linux 下的套接字和 Windows 下的命名管道)通过调用multiprocessing.Manager()),实际方法由运行在 SyncManager 的地址空间中的线程执行,侦听套接字连接,并且在方法调用完成之前不会回复连接(返回给调用者)。

更新

根据 OP 的更新问题,ab 列表的可能状态按时间顺序排列:

    a -> [], b -> [] a -> [1],b -> [] a -> [1],b -> [2]

没有其他可能的状态,因为对a.append(1) 的调用将阻塞,直到a 列表被附加值1。想象一下下面的代码:

a.append(1)
# How can the following assertion fail?
# Who would implement a list in such a way where this could fail?
assert(1 in a)
b.append(b)

【讨论】:

当我在 a.append(1) 后跟 b.append(2) 时,我希望在经过足够长的时间后找到 a == [1]b == [2],您可能希望在您的回答中更正这一点(你写了“......现在列表将是[1,2]”)但是如果过了很短的时间会发生什么还远非显而易见。如果管理器对象上有某种全局锁定,我确实希望唯一可能的可见状态是[],[][1],[][1],[2]。但总的来说,我很可能能够瞥见[],[2]。希望能澄清问题。 我的意思是在第一段中说a,append(2)(不是b.append(2))——我已经编辑了答案)。这就是为什么我说“现在列表将是[1, 2]”。所以在我回答的第二段中,我只是在处理其中一个列表。例如列表a,最终可能会假设两个进程都以该顺序将1和2写入a。所以请忘记b.append(2)。我没有对第二个列表中写入的内容做出任何假设。您还没有展示一个进程是如何对这两个列表进行修改的。例如,它们是否交错? 我现在使用您的示例对问题进行了澄清 我去看看。 作为竞争条件的一个很好的例子,这是您的“预编辑”示例,我们同时写入 a 和 b :-)

以上是关于在python中同步两个共享对象的读/写操作的主要内容,如果未能解决你的问题,请参考以下文章

大厂JAVA核心技能Java中的读/写锁

大厂JAVA核心技能Java中的读/写锁

大厂JAVA核心技能Java中的读/写锁

22Java并发性和多线程-Java中的读/写锁

[Go] golang的竞争状态

读写锁