动态多对象锁定 - 多线程

问题描述 投票:2回答:3

锁定不同(动态)数量的对象/键的最佳策略是什么?

考虑这样的场景,其中线程只能继续执行任务(事务),当在多个对象上获得锁定时(对象数组是动态的并且无法预测)。在此示例中,ID可以表示对象,需要将其作为“事务”的一部分进行修改。

例:

线程:要锁定的对象(作为事务的一部分)

T1:       A   B   C   D

T2:           B       D

T3:       A           D

编辑:改进示例

显然,执行顺序动态锁定,可能会导致所有线程死锁,因为T1可以锁定A,而T2锁定BT3抓住D锁定。 T1等待T2释放BT2等待T3释放DT3等待T1释放A

有哪些可能的选项来实现这种多对象锁定?

问题部分是理论上的,部分是实际的,因为它必须在C#/ .NET中解决

可能解决方案

为了保持并行性并保持正确的锁定,我想到了以下方案:

两个队列:

  1. 顺序队列(仅由1个线程提供,因此顺序)
  2. 并行队列(由线程池提供)

当请求到达N个对象时,检查每个对象Id以及每个ID的增量锁定计数(这可以是Dictionary<int, int> - <Id, Lock Count>)。

如果所有ID都被“锁定”(注意没有发生实际锁定),即第一次请求,则将请求放入并行队列ELSE将请求放入顺序队列

这种混合方法允许顺序处理“竞争”请求,而不是竞争 - 并行处理。

c# .net multithreading
3个回答
3
投票

锁定多个对象时防止死锁的方法是获取锁定的规范顺序。在您的示例中,让我们创建必须按字母顺序获取锁的方案。说T1获得AT2获得BT3不应该尝试获得D,直到它获得A。然后B可以获得D并完成。然后T1可以获得B,随后T3可以获得A

但是,在代码库中的任何地方都不能违反此方案。如果你已经获得了锁ABD,你就不能回去获得C

矛盾证明:

假设使用此方案可能会死锁。这意味着所有线程都在等待锁定。如果必须按顺序获取所有锁,则意味着所有锁都可以映射到整数1 ... N。一个获得的锁L必须是序列中获得的最高锁。但是,该线程无法阻止,因为没有线程可以拥有高于L的锁。因此,不可能阻止所有线程。


3
投票

我同意@Reed,如果有可能重新设计代码以避免所有这些锁 - 做到这一点。

一个非常简单的方法来重新设计它,因为你有这么多的锁,就是完全失去并发性。顺序运行一切。您可能会发现它的工作速度同样快,因为所有锁定都会阻止并发性。

如果由于某种原因无法做到这一点,那么确保不出现死锁的一种方法是始终以相同的顺序获取锁。在您的示例中,如果任务同时需要锁A和D,则始终在D之前锁定A.在所有其他任务中也执行此操作。

这种方式不可能陷入僵局。当任务1具有锁定A并且想要锁定B并且任务2具有锁定B并且想要锁定A时发生死锁。如果在锁定B之前总是锁定A,则任务2将无法锁定B然后想要锁定A.


2
投票

通常,您应该尽一切可能避免尝试以这种方式锁定多个对象。当您开始可选地锁定多个资源时,很难避免死锁。

相反,重新思考设计几乎总是更好的方法,并提出其他策略。例如,使用不可变类型可以避免在许多情况下完全锁定的需要。并发集合对于避免锁定也是非常有益的,因为您可以从生产中分离出数据的处理(生产者/消费者通过BlockingCollection<T>等)。

© www.soinside.com 2019 - 2024. All rights reserved.