为什么在锁内通过foreach循环遍历数组时使用yield会导致VS2010编译器崩溃?

7

关于Visual Studio 2010中使用的C#/.NET编译器的问题: 在项目开发过程中,同事遇到了这样一个情况,即在锁内使用现有代码时,VS2010编译器会崩溃。我们逐行分析代码,最终得出结论:在锁语句中通过foreach遍历数组时使用yield return会导致编译器崩溃。以下代码可以重现这个问题:

using System;
using System.Collections.Generic;

namespace PlayGround
{
    public static class Program
    {
        private static readonly object SyncRoot = new object();

        private static IEnumerable<int> EnumerableWithLock()
        {
            lock (SyncRoot)
            {
                foreach (var i in new int[1])
                {
                    yield return i;
                }
            }
        }

        public static void Main()
        {
            foreach (var i in EnumerableWithLock())
            {
                Console.WriteLine(i);
            }
        }
    }
}

我们在Visual Studio 2013上测试了这个复制样本,它没有出现同样的问题。这个编译器问题似乎与VS2010中使用的编译器有关,而在VS2012中可能会或可能不会出现同样的问题(我们无法访问它进行测试)。此外,我们已经测试过使用常规的for循环不会崩溃。因此,问题是,为什么VS2010编译器会崩溃?它到底在做什么以至于如此混乱?(是的,这主要是为了学习编译器而提出的问题。)

1
如果编译器实际上崩溃(您没有提及导致这种结论的症状),那么这就是一个简单明了的错误。lock、foreach和yield都是“神奇”的关键字,它们会被转换成其他一些代码片段,很可能这三者之间的相互作用(尤其是yield关键字,在这里可能涉及最多自动化代码)导致编译器存在错误。 - Lasse V. Karlsen
1个回答

3

好的,也许这并不能解决问题,但这是我的研究结果。

理论上,Jon Skeet说当锁在第一个和最后一个'MoveNext'迭代块之前和之后被获取和释放时,yield与锁一起使用不会产生任何问题。

更多信息请参见此处

当我个人尝试您的代码时,编译器抛出了以下(内部)错误:

Error   6   Internal Compiler Error (0xc0000005 at address 1332695D): likely culprit is 'TRANSFORM'.

An internal error has occurred in the compiler. To work around this problem, try simplifying or changing the program near the locations listed below. Locations at the top of the list are closer to the point at which the internal error occurred. Errors such as this can be reported to Microsoft by using the /errorreport option.
    ConsoleApp1

但是,对该类进行以下修改可以解决问题:
public static class Program
{
    //private static readonly object SyncRoot = new object();

    //private static IEnumerable<int> EnumerableWithLock()
    //{
    //    lock (SyncRoot)
    //    {
    //        foreach (var i in new int[1])
    //        {
    //            yield return i;
    //        }
    //    }
    //}

    public static void Main()
    {
        SomeClass sc = new SomeClass();
        foreach (var i in sc.EnumerableWithLock())
        {
            Console.WriteLine(i);
        }
        Console.ReadLine();
    }
}

public class SomeClass
{
    private static readonly object SyncRoot = new object();
    int[] i = { 1, 2, 3, 4, 5 };
    List<int> retval = new List<int>();

    public IEnumerable<int> EnumerableWithLock()
    {
        lock (SyncRoot)
        {
            foreach (var number in i)
            {
                retval.Add(number);
            }
            return retval;
        }
    }
}

也许只是CSC错误,或者更微妙的问题。

4
编译器不应该崩溃,因此这肯定是一个错误。 - Lasse V. Karlsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接