【问题标题】:Concurrent modification of linked list in CC中链表的并发修改
【发布时间】:2017-03-21 20:41:42
【问题描述】:

我已经编写了一个 C 应用程序来对 DNS 服务器(在 VM 上运行)进行压力测试,我正在尝试获得最大的 DNS 请求率(吞吐量)以查看它的行为。
我的应用程序运行两个线程,发送线程生成 DNS 请求(具有不同的查询名称)并将它们通过套接字发送到服务器,以及接收线程在同一套接字上接收 DNS 响应并计算响应时间。

现在使用并发链表(我已经实现),发送线程为每个发送的请求附加查询名称和发送时间(到列表的末尾)。接收线程每次从一开始就运行,查找具有相关发送时间的查询名称,计算响应时间并从列表中删除对象,它遍历的每个对象都检查发送时间是否在 DNS 超时窗口内设置,如果不是,我将此请求视为未答复并将其从列表中删除。

现在的问题是,每次在包含节点删除的接收线程中遍历整个列表时,我都必须锁定它,它严重影响停止发送的发送线程,等待大量时间锁定附加.

在我的情况下,您会建议以不同的方式实现锁定吗?或者也许我可以改变一些更重要的事情?

PS:我尝试在脚本中使用 unix dig 并在 python scapy 中实现相同但无法达到我在 C 应用程序中达到的速率(不锁定发送已到 10 Mbit),这就是我选择在 C 中实现它的原因。

谢谢!

【问题讨论】:

  • 您可以在时间戳上使用哈希,并为每个存储桶使用锁。您也可以尝试一种简单的读取时复制方法,即在读取之前复制列表。
  • 如果您使用带有单个事件分派循环的单线程模型,这可能不会更糟。然后可以在多个处理器上运行循环的多个实例(彼此独立)以生成更多请求。每个循环只处理 生成的回复,这些回复在它自己的私有链表中。这可能是fork-ed 到多个子进程。

标签: c multithreading concurrency concurrentmodification locks


【解决方案1】:

您可能不需要在接收线程扫描它时锁定整个列表。发送线程只会修改当前的最后一个节点(以追加一个新节点),只要它维护一个指向当前尾部的指针,它就不需要检查任何其他节点。就其本身而言,接收线程可以删除除当前尾部之外的任何节点,而不会对发送线程产生任何影响。因此只需要锁定尾节点。

在当前尾部保持移动锁可能有点棘手,因为您需要知道哪个节点,但我认为这不会太难。例如,您可以提供一个指向尾部的共享原子指针,发送者在发送每个请求之前更新它,而接收者在收到响应时读取它。接收方不需要考虑任何后续请求(因为在收到当前响应时它们尚未发送)。

接收者有时可能需要等待发送者附加至少一个请求,以便删除以前的尾部,但我希望这种情况很少见。否则,我看不出您的列表现在会变得如此之长,以至于在扫描时锁定整个内容太费时了。

【讨论】:

    猜你喜欢
    • 2012-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 2018-03-27
    • 2014-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多