【问题标题】:.NET Working with Locking and Threads.NET 使用锁定和线程
【发布时间】:2010-04-02 00:13:56
【问题描述】:

使用这个小型测试应用程序来学习线程/锁定。我有以下代码,我认为该行应该只写入控制台一次。但是,它似乎没有按预期工作。关于为什么的任何想法?我要做的是将此 Lot 对象添加到列表中,然后如果任何其他线程尝试点击该列表,它将阻塞。我在这里完全滥用了锁吗?

class Program
{
    static void Main(string[] args)
    {
        int threadCount = 10;

        //spin up x number of test threads
        Thread[] threads = new Thread[threadCount];
        Work w = new Work();
        for (int i = 0; i < threadCount; i++)
        {
            threads[i] = new Thread(new ThreadStart(w.DoWork));
        }
        for (int i = 0; i < threadCount; i++)
        {
            threads[i].Start();
        }

        // don't let the console close
        Console.ReadLine();
    }
}

public class Work
{
    List<Lot> lots = new List<Lot>();
    private static readonly object thisLock = new object();

    public void DoWork()
    {
        Lot lot = new Lot() { LotID = 1, LotNumber = "100" };
        LockLot(lot);
    }

    private void LockLot(Lot lot)
    {
        // i would think that "Lot has been added" should only print once?
        lock (thisLock)
        {
            if(!lots.Contains(lot))
            {
                lots.Add(lot);
                Console.WriteLine("Lot has been added");
            }
        }
    }
}

【问题讨论】:

  • 为什么你认为它应该只打印一次?

标签: c# .net multithreading locking


【解决方案1】:

lock 语句确保两段代码不会同时执行。

如果两个线程同时进入lock 块,第二个线程将等待第一个线程完成,然后继续执行该块

在您的代码中,lots.Contains(lot) 始终为false,因为DoWork 方法在每个线程中创建不同的Lot 对象。因此,每个线程在获得锁后,又增加了一个Lot对象。

您可能希望在您的Lot 类中覆盖EqualsGetHashCode 并使其按值进行比较,以便lots.Contains(lot) 将为具有相同值的不同Lot 对象返回true

【讨论】:

  • @Erup:实际上,List&lt;T&gt;.Contains 不使用GetHashCode。但是,它仍然应该(正确地)被覆盖。
【解决方案2】:

lock 本质上是一个临界区,只会在其中的代码执行时锁定对象。只要代码存在lock 块,对象就会被解锁。所以......每个线程都会(最终)打印到控制台是有道理的。

您正在每个线程上创建一个新的 Lot 对象,因此如果您没有为该对象定义自己的 Equals 方法,那么lots.Contains(lot) 将始终返回 false 是有意义的。

【讨论】:

    【解决方案3】:

    您需要 lock 语句来保护共享数据,同时由多个线程读取和写入的变量。 “lot”变量不符合该要求,每个线程都创建自己的 Lot 对象实例。并且引用存储在一个局部变量(“lot”)中,每个线程都有自己的局部变量。

    地块字段确实符合要求。它只有一个实例,因为Work类只有一个实例,所有线程都访问它。 And 线程分别通过 Contains 方法和 Add 方法读取和写入列表。您的 lock 语句阻止线程同时访问列表并且是正确的, Contains 永远不能与 Add 同时运行。

    您已经完成了 95%,您只是错过了每个线程都有一个唯一的“批次”对象。以前不能添加到列表中的一个。因此,每个线程都会从 Contains 获得错误返回。

    如果您希望 Lot 类具有标识,基于 LotID 和 LotNumber 属性值而不仅仅是对象实例,那么您需要通过覆盖 Equals() 和 GetHashCode() 方法来为其提供标识。查看你最喜欢的 C# 编程书籍,他们都提到了这一点。否则它与线程无关。

    【讨论】:

      【解决方案4】:

      为什么您只希望它运行一次?您在 10 个不同的线程中调用 DoWork,每个线程都创建自己的“new Lot()”对象。您是否期待 Lot 对象的价值比较?您是否重写了 Equals() 并实现了 IEquatable?

      【讨论】:

      • 这么多问题……作者求你解答!
      • 作者说他正在尝试学习线程。提出引导性问题是一种有用的教学技巧。
      • 是的,当引导性问题指向答案时,它们很有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-07
      • 2011-10-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多