使用Parallel.For()的AccessViolationException

时间:2014-08-25 09:46:29

标签: c# crash task-parallel-library parallel-extensions

下面的代码片段展示了我能够从生产代码中隔离出来的错误。程序将因System.AccessViolationException而崩溃。

我的系统配置是Windows 8.1 x64上的Visual Studio 2013。若要重现该错误,请在Visual Studio中按照下列步骤操作:

  1. 创建一个空的控制台应用程序
  2. 用下面的代码替换Program.cs的全部内容
  3. 发布模式下编译,任何CPU
  4. 启动不调试(CTRL + F5)
  5. 程序将立即崩溃,在控制台窗口中显示错误堆栈跟踪。

    我无法进一步减少代码,即修改代码的任何部分都会使错误消失。

    有没有办法告诉这个bug的根本原因?什么是一个好的解决方法?

    using System;
    using System.Threading.Tasks;
    
    namespace ParallelBugTest
    {
        class Program
        {
            private class Value
            {
                public double X;
            }
    
            private class Aggregator
            {
                private Value myval = new Value();
    
                public void Add()
                {
                    // use Min() instead of Max() -> bug disappears
                    myval.X = Math.Max(0, myval.X);
                }
            }
    
            public static void Main(string[] args)
            {
                Parallel.For(0, 10000, Process);
            }
    
            private static void Process(int k)
            {
                Value[] V = new Value[10000];
                Aggregator aggregator = new Aggregator();
    
                for (int i = 0; i < V.Length; i++)
                {
                    V[i] = new Value();
                    aggregator.Add();
                }
            }
        }
    }
    

2 个答案:

答案 0 :(得分:0)

代码中的所有内容都是线程安全的,因为线程之间没有共享状态(每次迭代都有自己的AggregatorValue的数组,并且您没有使用任何static 1}}字段)。

即使您的代码不是线程安全的,它也不会做任何不安全的事情(即直接使用内存),这应该是获取AccessViolationException的唯一方法。

因此,我认为这是CLR中的一个错误,您应该report it(登录后点击“提交反馈”链接)。

答案 1 :(得分:-1)

Math.Max不是线程安全的。我想Min正在工作,因为计算速度更快。 锁定Max-Call有效:

    private static Object lockObj = new Object();

    private class Value
    {
        public double X;
    }

    private class Aggregator
    {
        private Value myval = new Value();

        public void Add()
        {
            // use Min() instead of Max() -> bug disappears
            lock (lockObj)
            { 
                myval.X = Math.Max(0, myval.X);
            }
        }
    }

    public static void Main(string[] args)
    {
        Parallel.For(0, 10000, Process);
    }

    private static void Process(int k)
    {
        Value[] V = new Value[10000];
        Aggregator aggregator = new Aggregator();

        for (int i = 0; i < V.Length; i++)
        {
            V[i] = new Value();
            aggregator.Add();
        }
    }

快速而简单的方法就是使用普通的iif:

        public void Add()
        {
            // use Min() instead of Max() -> bug disappears
            myval.X = myval.X > 0 ? myval.X : 0;
        }