C#很简单,为什么它没有产生正确的结果

本文关键字:结果 简单 为什么 | 更新日期: 2023-09-27 18:21:04

有人能告诉我为什么下面的结果不正确吗?它给了我1233,而我预期的是0123。

    public static readonly object locker = new object();
    public static List<int> queue = new List<int>();
    public static void calculate(int input)
    {
        Thread.Sleep(1000);
        lock (locker)
        {
            queue.Add(input);
        }
    }
    [TestMethod]
    public void TestT()
    {
        int[] _intList = new int[] { 0, 1, 2, 3 };
        List<Thread> _threadList = new List<Thread>();
        foreach (int num in _intList)
        {
            Thread t = new Thread(() => calculate(num));
            t.Start();
            _threadList.Add(t);
        }
        foreach (Thread t in _threadList) { t.Join(); }
        foreach (var t in queue)
        {
            Console.WriteLine(t);
        }
    }

当我将其更改为使用_intList变量的副本时,我得到了0123的正确结果。有人能告诉我为什么会发生这种事吗?它被缓存在什么地方了吗?

        foreach (int num in _intList)
        {
            int testNum = num;
            Thread t = new Thread(() => calculate(testNum));
            t.Start();
            _threadList.Add(t);
        }

C#很简单,为什么它没有产生正确的结果

当您将变量传递给lambda表达式时,它会被表达式捕获。所以这不是一个副本,而是你得到的变量。这是foreach和延迟执行(是否为多线程)的常见问题,因为foreach continue num正在获取它的下一个值,如果它这样做是因为线程要计算,那么将计算该值。

如果你没有对其进行多线程处理,而是在foreach之后调用lambda的结果,你会看到的结果是3 3 3 3,在这种情况下,你只是看到它们一个接一个的集合,因为启动线程所需的时间很可能与1次迭代相同。

当您复制变量时,该变量在foreach的范围内声明,并且每次都是一个新变量,它不会被更改为下一个成员,而该变量是被捕获的变量,从而为您提供正确的结果。这是正确的方法。你得到的结果并不意外,但也不能保证,你可以用第一个方法得到从0 1 2 3到3 3 3的任何结果,第二个方法可以保证正确的输出。