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

标签 c# .net multithreading thread-safety threadpool

我写过很多多线程 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/

相关文章:

c# - Windows Phone 8 消息框问题

c# - 在 C# 中选择搜索结果的类型

c# - 为什么 Dictionary.ContainsKey 抛出 ArgumentNullException?

python - 多线程一定会减少运行时间吗?

c - 在Linux中,pthread_exit()和pthread_cancel()如何调用清理例程?

c# - UWP 导航 Pane 问题

c# - 您可以使用 C# 线程同步以原子方式发出信号并等待吗?

c# - .Net EntityFramework 4.0 中类似数据库表的继承

c# - 如何在 C# 中将日期格式转换为 DD-MM-YYYY

python - 如何等待python中动态启动的多个线程完成