【问题标题】:Detecting circular loop in concurrent queue检测并发队列中的循环循环
【发布时间】:2012-04-24 20:42:05
【问题描述】:

我有一个并发的连接队列。有一个 ping 任务周期性地遍历队列中所有可用的连接以执行 ping,同时应用程序可以请求队列中的连接以供其使用。

什么是检测循环循环的好方法,以便 ping 任务可以完成其当前执行?

编辑:

示例:假设并发队列有连接 A、B 和 C。

有一个 ping 任务遍历队列。所以在这个例子中,它将执行A.ping()B.ping()C.ping()。现在有一个外部类,它也请求队列中的连接,同时 ping 任务也在遍历队列。所以假设 A.ping() 已经完成并且externalClass.getConnection() 将返回A。当 ping 任务完成 C.ping() 时,externalClass.releaseConnection(A) 发生了。现在队列的顺序是B,C and A。所以 ping 任务会在完成 C.ping() 之后再次找到 A,此时任务必须确定 A 已经被 ping 过并且应该完成当前的执行。

【问题讨论】:

  • 请举例说明发生循环的情况

标签: algorithm concurrency queue


【解决方案1】:

对于初学者,您没有循环。

循环是指你有一个指向自身的链表。您只有一个队列,当您单步执行它时,可能会有另一个线程将重复的元素添加到队列的末尾。

检测循环循环的方法是使用hashtable,在每个Ping()之前检查连接是否已经存在于连接中,如果存在,则继续移动到下一个元素,如果不存在'不存在然后你将它添加到hashtable 然后你调用你的Ping 操作到服务器。

或者您可以让您的初始ping 操作在该确切时间对队列进行快照,而不是越过它。

话虽如此,如果您可以让两个不同的呼叫者从队列中获得相同的A 结果,那么您的队列就有问题了。

执行此代码的正确方法是有两个单独的列表,一个是请求的连接队列,例如externalClass.getConnection(),另一个是Ping 的连接列表。无论您的Ping 操作在做什么,它都不应该影响任何外部类对其连接所做的任何事情,例如假设connectionsql connection,那么Ping 应该执行如下操作:

SELECT TOP 1 1

仅此而已,这将意味着您的连接仍然存在。因为您可能会实施此操作,因为连接空闲时间过长并且会自行关闭。在这种情况下,您真的不应该这样做,因为几乎任何类型的 sql dbms 都支持连接池,这正是您想要做的。除非您想通过让其他执行等待来阻止多个 Queue.Count 并发连接(这本身可以更好地保持打开连接池,例如简单的int counter

除非您保持与不同服务器的连接并尝试通过在多个服务器之间轮换请求来进行一些临时负载平衡,否则请使用我上面的解决方案以及所有连接列表和可用连接队列。此解决方案的主要好处是您可以在应用程序关闭时终止所有连接,即使它们当前正在处理请求。

但是,为了完整回答您的问题:

如果您确实有一个循环,它看起来像这样:A -> B -> C -> A,其中每个元素将指向列表中的下一个元素,而不仅仅是queue 中的元素。一个很好的例子是你 Ping server A which ping server B which ping server C which then ping A 你会检测到如下:

检测循环的简单方法是一次运行两次(或多次)迭代,我们将它们称为 X 和 Y。

每第二次踏入(或在您的上下文中.Ping())X,您就踏入一次 Y。您可能想要创建一个新方法,例如 Visit,而不是调用您的 Ping,以便在循环中不会多次调用 ping。

假设队列看起来像A, B, C, A, B...

经过几个步骤后,X 看起来像:A, B, C, A,而 Y 看起来像 A, B。您所做的是存储整个历史记录,您只查看当前值,因此当您进入 X 时,您会检查新值是否与 Y 的当前值匹配,因此我们将最终总会发生碰撞。

这不是检测循环回路的最快或最有效的方法,但它是最简单的,如果您的回路通常很小,它比存储过去路线的历史列表更容易(在某些情况下需要大量代码更改)。当循环长度超过 20 步时,可以使用更有效的算法(它们旨在处理复杂的分支树等)。重要的是要认识到这种实现的最坏情况将是循环元素的质数。

但是您可以通过进一步扩展它来提高平均性能@steps 或 7 等等(随着每个新迭代器的创建访问下一个递增的素数)。

【讨论】:

  • 连接不是JDBC连接而是thrift连接,ping()是thrift服务方法。是的,现在我正在使用类似于您建议的解决方案,使用 HashSet 而不是 HashTable。但我发现很难理解您对使用 2 个列表的解释。我希望打开的连接保持活动状态,以便外部类可以使用它。
【解决方案2】:

你的 ping 是只 ping 每台机器,还是 ping 一台机器,然后再 ping 下一台,然后再 ping 下一台,然后以此类推?

解决此问题的一种方法是让每台机器跟踪其是否已被 ping。如果是,并且它接收到新的 ping,它不会再次发送 ping。

解决这个问题的另一种方法是传递有关给定 ping 数据包已经看到的机器的信息,当 ping 数据包到达已经看到的机器时,它会终止。

【讨论】:

  • 连接池有所有连接到同一台机器,这意味着是的,所有连接都ping同一台机器。
  • 那有什么问题呢?当你ping它时,机器会ping你吗,还是什么?你说的循环是什么意思?
【解决方案3】:

也许我不明白您在那里做什么,但在我看来,问题在于 Ping 任务不像外部任务那样遵循入队/出队规则。那么让 Ping 任务先出队一个连接,刷新它的“ping 值”,然后再入队呢?

如果您的目标是确保外部任务始终获得具有最小 ping 值的连接,那么您可能希望使用堆而不是队列。

【讨论】:

    猜你喜欢
    • 2012-08-05
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 2014-01-05
    • 1970-01-01
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多