【问题标题】:Understanding the thread safety of a List<T>了解 List<T> 的线程安全
【发布时间】:2021-11-26 20:01:45
【问题描述】:
  1. 我试图理解为什么它会打印第 0 项、第 0 项和第 1 项
  2. 在调试时会打印第 0 项、第 0 项、第 1 项、第 1 项

2 结果中的项目符号上方有意义。有人可以帮我理解为什么它会在 1 中打印项目符号吗?

简而言之,取自 C# 9.0

class ThreadSafe
{
    static List<string> _list = new List<string>();

    public static void AddItem()
    {
        // lock the list
        lock (_list)
        {
            _list.Add("Item " + _list.Count);
        }
        // Rather than locking for the duration; copy to an array
        string[] items;
        lock (_list)
        {
            items = _list.ToArray();
        }
        foreach (string s in items)
        {
            Console.WriteLine(s);
        }
     }
     static void Main(string[] args)
     {
       new Thread(ThreadSafe.AddItem).Start();
       new Thread(ThreadSafe.AddItem).Start();
     }
}

【问题讨论】:

  • 输出不会取决于运行线程的计算机的速度和调度吗?
  • 我只看到“项目 1”的一个实例 dotnetfiddle.net/8oHjcz 如果您在打印中包含线程号,代码是否有意义?
  • 您能否说明您希望输出的哪一部分有所不同以及为什么?也许您希望一次性执行对Console.WriteLine 的所有单独调用? (@gunr2171 建议在所有打印语句中添加线程号可以帮助您澄清您的要求,甚至完全回答)
  • 使用 ThreadID,我可以看到第一个线程打印项目 0,然后第二个线程打印项目 0。然后第二个线程打印项目 1,并完成执行。我试图理解为什么第二个线程在第一个线程执行后打印项目 0。好像是同时进行的。
  • 老实说,约瑟夫·阿尔巴哈里书中的这个例子不是很好。产生的输出令人困惑。我希望他们能在下一个版本的书中改进它!

标签: c# multithreading thread-safety


【解决方案1】:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement

当持有锁时,持有锁的线程可以再次获取和释放锁。任何其他线程都被阻止获取锁并等待直到锁被释放。

这确实取决于环境,但直截了当的答案如下。

  1. 线程 1 将锁定列表。
  2. 线程 2 将尝试访问但无法访问,因此它将等待
  3. 线程 1 将添加到列表中
  4. 线程 2 仍在等待
  5. 线程 1 将释放锁
  6. 线程 2 将锁定列表
  7. 线程 1 将尝试锁定列表,但它已被锁定,因此它将等待
  8. 等等

这真的取决于线程何时真正开始计算。但这将是您所看到的输出的最合乎逻辑的答案。在不同的环境(CPU 等)上运行它可能会导致不同的输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多