gpt4 book ai didi

c# - 如果在原始数据访问周围只使用 lock 关键字,是否有可能在 C# 中创建死锁?

转载 作者:行者123 更新时间:2023-11-30 19:38:48 25 4
gpt4 key购买 nike

我写过很多多线程 C# 代码,在我发布的任何代码中从未出现过死锁。

我使用以下经验法则:

  1. 我倾向于只使用 lock 关键字(我也使用其他技术,例如读取器/写入器锁,但很少使用,并且仅在需要提高速度时使用)。
  2. 如果我正在处理一个long,我会使用Interlocked.Increment
  3. 我倾向于使用最小粒度的锁定单元:我只倾向于锁定原始数据结构,例如 longdictionarylist.

我想知道是否有可能产生死锁如果这些规则始终得到遵守,如果是这样,代码会是什么样子?

更新

我也使用这些经验法则:

  1. 避免在任何可能无限期暂停的事物周围添加锁,尤其是 I/O 操作。如果您绝对必须这样做,请确保锁中的所有内容在设置的 TimeSpan 后绝对超时。
  2. 我用于锁定的对象始终是专用对象,例如object _lockDict = new object(); 然后 lock(_lockDict) {//在此处访问字典 }

更新

Jon Skeet 的出色回答。这也证实了为什么我永远不会遇到死锁,因为我倾向于本能地避免嵌套锁,即使我确实使用它们,我也总是本能地保持输入顺序一致。

为了回应我关于倾向于只使用 lock 关键字的评论,即使用 Dictionary + lock 而不是 ConcurrentDictionary,Jon Skeet 发表了评论:

@Contango: That's exactly the approach I'd take too. I'd go for simple code with locking over "clever" lock-free code every time, until there's evidence that it's causing an issue.

最佳答案

是的,很容易死锁,而无需实际访问任何数据:

private readonly object lock1 = new object();
private readonly object lock2 = new object();

public void Method1()
{
lock(lock1)
{
Thread.Sleep(1000);
lock(lock2)
{
}
}
}

public void Method2()
{
lock(lock2)
{
Thread.Sleep(1000);
lock(lock1)
{
}
}
}

同时调用 Method1Method2,然后 boom - 死锁。每个线程都将等待“内部”锁,其他线程已将其作为其“外部”锁获取。

如果您确保始终以相同的顺序获取锁(例如“永远不要获取 lock2,除非您已经拥有 lock1)并以相反的顺序释放锁(如果您使用 lock 获取/释放,这是隐式的)那么您将不会遇到那种死锁。

仍然可以在异步代码中遇到死锁,只涉及一个线程——但这也涉及Task:

public async Task FooAsync()
{
BarAsync().Wait(); // Don't do this!
}

public async Task BarAsync()
{
await Task.Delay(1000);
}

如果您从 WinForms 线程运行该代码,您将在单个线程中死锁 - FooAsync 将阻塞 BarAsync 返回的任务,并且继续BarAsync 将无法运行,因为它正在等待返回到 UI 线程。基本上,您不应该从 UI 线程发出阻塞调用...

关于c# - 如果在原始数据访问周围只使用 lock 关键字,是否有可能在 C# 中创建死锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31192914/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com