Haskell 线程通信模式场景
你有两个线程,a 和 b。线程 a 处于永远循环中,监听阻塞套接字 1。线程 b 也处于永远循环中,监听阻塞套接字 2。套接字 1 和套接字 2 都可能在任意时间返回数据,因此线程 a 可能会永远休眠等待数据,而线程 b 不断从套接字获取数据并继续处理。这就是背景。
现在假设他们需要共享一本字典。当线程 a 获取一些数据(如果有)时,它会在经过一些处理后将键值对添加到字典中,然后继续等待更多数据。当线程 b 从其套接字接收数据时,它首先查询字典以查看是否存在与其接收到的数据相关的信息,然后再继续处理。字典没有删除,只有插入和查询(如果这对最终解决方案产生影响,我很感兴趣)。
在像 python 或 c 这样的标准命令式语言中,通过使字典在两个作用域中都可用并且仅在线程获取锁后查询它,这很容易做到,因此线程 B 总是看到最新的(几乎)最新的字典。
在 Haskell 中,我似乎正在努力想出这种模式的良好实现。 MVar 一次只能有一项,因此线程 a 不能将其放入字典中,因为可能会发生新的更新,并且在线程 b 从 MVar 中获取新字典之前,它无法推送该新字典。另一方面,如果线程 b 使用 MVar 发送就绪信号“ok!”对于线程a,可能会出现线程a正在其读套接字上休眠的情况,因此在其读套接字解除阻塞之前,它将无法发送回数据!还有通道,但这看起来很混乱,因为我必须不断发送新词典,而线程 B 会丢弃除最后一个之外的所有词典。
另一种可行的解决方案是简单地沿着通道发送更新,并让线程 B 为自己构造字典。不过我想知道是否有更好的替代解决方案。
感谢您花时间阅读这个很长的问题!
You have two threads, a and b. Thread a is in a forever loop, listening on a blocking socket 1. Thread b is also in a forever loop, listening on blocking socket 2. Both socket 1 and socket 2 may return data at arbitrary times, so Thread a may be sleeping forever waiting for data whereas Thread b constantly gets data from the socket and goes on with its processing. That's the background.
Now suppose they need to share a dictionary. When Thread a gets some data (if ever) it adds a key value pair into the dictionary after some processing, and then continues to wait for more data. When Thread b receives data from its socket it first queries the dictionary to see if there is information related to the data it has received before going on with its processing. There are no deletions to the dictionary, only inserts and queries (I'd be interested if this makes a difference in the end solution).
In a standard imperative language like python or c, this is pretty easy to do by making the dictionary available in both scopes and only querying it after a thread has acquired a lock, so Thread B always sees the most (well almost) up to date dictionary.
In Haskell, I seem to be struggling to come up with a good implementation of this pattern. MVars, can only have one item at a time so it can't be that Thread a puts in the dictionary, since a new update might occur and it would not be able to push that new dictionary until Thread b fetched it from the MVar. On the other hand if thread b uses an MVar to send a ready signal "ok!" to thread a, it may be the case that Thread a is sleeping on its read socket, so it would be unable to send back data until its read socket unblocked! There are also channels, but that seems messy since I would have to keep sending new dictionaries and Thread B would discard all but the last one.
The alternative solution that would work is to simply send the updates down a channel, and have thread B construct the dictionary for itself. However I'm wondering if there are better alternative solutions.
Thanks for taking the time to read this very long question!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以通过以下方式使用
MVar
:takeMVar
获取字典。当成功时,它会更新字典并将其放回MVar
takeMVar
获取字典 - 在上述场景中,A很少会获得平均而言应该很快成功的数据。然后它进行查找并将字典放回原处。正如 hammar 指出的那样,最好不要直接使用
takeMVar
和putMVar
而是将它们分别包装在modifyMVar_
中。modifyMVar
如果一个线程在使用字典时发生异常,则不会将MVar
保留为空。在线程 A 中,就像在线程 B 中一样
,您所需要的只是一个简单的
readMVar
(再次感谢 @hammar 指出这一点)。You can use an
MVar
in the following way:takeMVar
. When that succeeds, it updates the dictionary and puts it back into theMVar
takeMVar
- in the above scenario where A seldom gets data that should succeed rather quickly on average. Then it does the lookup and puts the dictionary back.As hammar pointed out, it's probably better to not directly use
takeMVar
andputMVar
but rather wrap them inmodifyMVar_
resp.modifyMVar
to not leave theMVar
empty if one thread gets an exception while using the dictionary.In thread A, something like
in thread B all you need is a simple
readMVar
(thanks to @hammar again for pointing that out).