多线程死锁问题

时间:2020-03-23 19:26:11

标签: c# multithreading deadlock

更新: 我不是StackOverflow的普通用户,因此表示歉意,但是我看不到将其私下发送给主持人的方法 我认为将这个线程锁定为主题是非常不公平且繁重的工作,并说我必须发布示例代码等。我已经解释说它是一个大型复杂的应用程序,我将不得不发布数十行代码,所有人都无法理解。并非所有软件问题都足够简单,可以归结为10行示例代码!

基本问题是体系结构问题,我尽力解释了我有2个异步线程需要锁定两个资源,然后这2个锁也是 在主线程中访问。由于异步的性质。我看不到如何控制锁的顺序和两个锁的交错以始终保证没有死锁。

是的,正如我所建议的,我只能做一个锁来锁定所有东西,但是性能下降会非常糟糕,所以我不能真正选择这个简单的解决方案。 我希望当异步场景中需要多个锁时,有一种性能更高的经典解决方案。

无论如何,感谢您在关闭之前得到的答复,我感谢您的宝贵时间,并在这些困难时期给予帮助。

致谢


我正在用C#编写一个相当大的应用程序,它有2个异步线程,一个线程接收商品价格数据流,而一个线程接收订单明细流。

这些线程中的每一个都使用它们自己的ReaderWriterLockSlim构建传入价格/订单数据的列表。将传入数据移到列表时,设置为写锁定模式。

传入流的频率变化很大且无法预测,有时商品价格数据包可能以10 ish millisec的间隔进入,因此我需要对此进行有效编码,否则它将变得非常缓慢。

最初,我只是在读取主线程中的列表(ReaderWriterLockSlim读取锁),这很好用。 但是,我现在有一个要求,有时我需要在主线程中对这两个数据列表进行一些写操作。 是的,您猜对了,有了额外的写锁,我现在得到了随机死锁,我可以肯定这是由交错访问2个独立锁引起的经典死锁。

由于传入数据的异步特性,相对于我的主线程在两个列表上进行的处理,我无法预测何时将这些线程写入或读取以锁定列表。

据我所知,这总是很容易陷入僵局。我为如何更改体系结构以解决此死锁问题而深感困惑。

如果有人可以就如何解决此问题提出一些想法,我将非常感激。


代码示例。一个线程运行以下代码:

lock (locker1)
{
    list1.Add("Something");
    lock (locker2)
    {
        list2.Add("Something");
    }
}

...同时,另一个线程运行此代码:

lock (locker2)
{
    list2.Add("Something");
    lock (locker1)
    {
        list1.Add("Something");
    }
}

是否可以避免随后的死锁,而无需按获取锁的顺序引入限制?

2 个答案:

答案 0 :(得分:2)

(1)在描述代码而不是显示代码时,很难说出问题出在哪里。

(2)在以下情况中> 80%的情况下,当您有多个由不同线程以错误顺序锁定的锁时发生死锁:例如,您拥有A,B,C锁,并且线程1尝试为A收集锁, B,C同时运行,线程2尝试为B,A,C收集锁。

确保所有线程以相同顺序锁定其锁。即使其中一个线程不需要“ A”,也要确保其他锁按顺序进行。

(请注意,在下面的示例中,ABC意味着线程试图在释放任何对象之前先将它们全部占用。这意味着“锁定A,锁定B,锁定C,执行某些操作,释放所有三个”)

这些可能会不时陷入僵局:

  • 线程1:AB,线程2:BA
  • 线程1:ABC,线程2:BAC
  • 线程1:ABC,线程2:CAB

那是因为一个线程有可能占用B并等待A,而另一个线程有可能占用A并等待B。

另一方面,它们永远不会死锁:

  • 线程1:A,线程2:AB
  • 线程1:A,线程2:ABC
  • 线程1:AB,线程2:BC
  • 线程1:ABC,线程2:ABC
  • 线程1:ABC,线程2:AC
  • 线程1:BC,线程2:ABC

这是因为严格按照相同的顺序进行锁定。线程无法在已经持有B的情况下尝试使用A,依此类推。

在不太明显的情况下,基本规则是严格锁定尝试进行操作的顺序可以防止死锁,因此可以:(a)一次仅锁定一件事,然后在进行任何其他锁定之前将其解锁,或者( b)确保始终按严格顺序获得给定操作所需的所有资源-您有一个锁定三个列表的操作?确保这些列表始终以相同的顺序锁定。随机需要​​100个清单中的3个吗?没关系。订购它们。确保它们以相同顺序锁定。如果有困难,请使用其ID,名称甚至地址,无论如何,只要确保没有线程尝试锁定列表(N + 1)之前的列表(N + 1)。这是一个简单的解决方案,可能不是最佳性能,但它至少提供了一个很好的安全起点,它不会强制执行完整的序列化等,您可以在其中尝试其他方案和/或诊断其他问题。 / p>

答案 1 :(得分:-1)

我可以想到的一种简单方法是在主线程中维护一条消息队列,其中每条消息将包含您拥有的两个流之一中的流,并且您可以根据另一个参数来区分这两个流在消息中。

这样,您一次将处理一个流,并且不会遇到任何死锁问题。

相关问题