首先对我要做的事情做一点解释:
我的计划是使用boost :: asio库编写一个带有套接字流的程序,该库将数据提供给使用boost:spirit :: qi实现的解析器。解析器将获取数据包并填充数据包对象,然后将该对象附加到数据包对象的链接列表的末尾。数据包处理器将读取列表中的第一个对象并进行处理,然后移到下一个项目并删除第一个。
我决定使用一个链表,因为如果我使用了std :: queue,每次流添加一个数据包或处理器删除一个时,我都必须锁定整个容器,这会使两个线程运行或多或少连续,我想避免。此外,队列类具有复制整个对象的倾向,而链表概念具有创建对象一次然后仅指向它的好处。为了避免序列化整个业务,我打算在每个节点中放置boost:互斥互斥锁并从那里锁定它们。我们的想法是让套接字流创建列表并立即锁定第一个节点,从解析器填充节点,创建下一个节点并锁定它,然后解锁第一个节点并转到下一个节点继续工作。这种方式从来没有一个解锁节点悬挂在最后,数据包处理器可以跳转到套接字流鼻子下面并删除。数据包处理器将检查第一个节点并尝试锁定它,如果它锁定它,那么它将执行其处理然后解锁它,获取下一个节点然后删除第一个节点。这种方式序列化仅限于数据包处理器赶上套接字流类的那些时间。
所以现在我的问题是,在我实际执行此工作之前,这听起来像个好主意吗?我已经尝试了一个简单的测试,它似乎工作正常,我想不出任何严重的问题,只要我实现异常处理,并注意释放我分配的任何内存,但如果有人能想到我忽略了这个想法的任何问题我会很感激输入。此外,我将非常感谢任何人可能有任何其他建议作为替代或可能使这个想法更好地工作。
提前致谢!
答案 0 :(得分:1)
查看这篇文章,它是关于多个消费者的,但仍然很棒:
Measuring Parallel Performance: Optimizing a Concurrent Queue
答案 1 :(得分:0)
我认为你的建议并不奏效。请记住,从链接列表中删除节点时,需要更新指向已删除节点的其他节点。同样,添加节点时,还需要更新其他节点以指向新节点。所以只是锁定被删除或添加的节点是不够的。
有无锁队列,但是他们很难得到正确的答案。例如,查看文章here的初始评论,其中描述了使已发布算法工作所需的额外工作。
答案 2 :(得分:0)
即使你将其称为链表,这实际上也是一个队列。
如果您愿意使用固定大小的缓冲区,则可以实现Single Producer Single Consumer无锁队列。这使您可以控制内存使用量,但是如果消费者不够快,则以使生产者等待为代价。
除了这一点,你的设计看起来很好;它可能比无锁替代方案更容易实现。
请记住有终止条件(例如next
字段中的空指针),以便生产者可以向消费者发出信号,表明没有其他事情需要处理。
答案 3 :(得分:0)
这个实现在我身上尖叫着三件事:
这看起来像是过早优化和过早悲观并行运行的情况。什么推动了这种架构?
我建议先从简单的解决方案开始。每次想要触摸任何部分时都要锁定整个物体。看看是否会让你陷入困境。如果没有,问题就解决了。如果是这样,下一步就是从互斥锁切换到读写锁。只有一个,而不是一个巨大的。现在你必须考虑一下你是想要共享锁还是独占锁。如果你一直在努力学习const-correctness,请尝试为const方法使用共享锁(读锁),为非const方法使用独占锁(写锁)。
答案 4 :(得分:0)
从您的流中获取字节,直到您根据协议组装了“数据包对象”。将数据包对象 reference 推送到队列中,然后立即将new()推送到下一个数据包。数据包处理器使用队列中的数据包对象。
队列仅在从队列中推送/弹出对象引用所花费的时间内被锁定。由套接字/流回调组装的包对象和由包处理器处理的包对象总是不同的,因此不需要锁定。
尝试对已经在队列中的对象(或类似队列的链表)进行操作,对我来说听起来像是一场噩梦,(其他海报似乎也同意)。还有其他原因需要这样做吗?
RGDS, 马丁