下面的代码片段展示了我能够从生产代码中隔离出来的错误。程序将因System.AccessViolationException
而崩溃。
我的系统配置是Windows 8.1 x64上的Visual Studio 2013。若要重现该错误,请在Visual Studio中按照下列步骤操作:
程序将立即崩溃,在控制台窗口中显示错误堆栈跟踪。
我无法进一步减少代码,即修改代码的任何部分都会使错误消失。
有没有办法告诉这个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();
}
}
}
}
答案 0 :(得分:0)
代码中的所有内容都是线程安全的,因为线程之间没有共享状态(每次迭代都有自己的Aggregator
和Value
的数组,并且您没有使用任何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;
}