C# AccessViolationException与Parallel.For()一起使用
下面的代码片段展示了我能够从生产代码中分离出来的一个bug。程序将因C# AccessViolationException与Parallel.For()一起使用,c#,crash,task-parallel-library,parallel-extensions,C#,Crash,Task Parallel Library,Parallel Extensions,下面的代码片段展示了我能够从生产代码中分离出来的一个bug。程序将因系统崩溃。AccessViolationException 我的系统配置是Windows 8.1 x64上的Visual Studio 2013。要重现该错误,请在Visual Studio中执行以下步骤: 创建一个空的控制台应用程序 用以下代码替换Program.cs的全部内容 在发布模式下编译,任何CPU 在不调试的情况下启动(CTRL+F5) 程序将立即崩溃,在控制台窗口中显示错误堆栈跟踪 我无法进一步减少代码,即修改代码
系统崩溃。AccessViolationException
我的系统配置是Windows 8.1 x64上的Visual Studio 2013。要重现该错误,请在Visual Studio中执行以下步骤:
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();
}
}
}
}
使用系统;
使用System.Threading.Tasks;
命名空间并行错误测试
{
班级计划
{
私有阶级价值
{
公共双X;
}
私有类聚合器
{
私有值myval=新值();
公共无效添加()
{
//使用Min()代替Max()->错误消失
myval.X=Math.Max(0,myval.X);
}
}
公共静态void Main(字符串[]args)
{
平行。对于(0,10000,过程);
}
私有静态无效进程(int k)
{
值[]V=新值[10000];
聚合器聚合器=新聚合器();
对于(int i=0;i
Math.Max不是线程安全的。我想Min在工作,因为它计算起来更快。
锁定Max调用会起作用:
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();
}
}
代码中的所有内容都是线程安全的,因为线程之间没有共享状态(每个迭代都有自己的
聚合器
和值
数组,并且不使用任何静态
字段)
即使您的代码不是线程安全的,它也不会做任何不安全的事情(即直接使用内存),这应该是获取AccessViolationException
的唯一方法
因此,我认为这是CLR中的一个bug,您应该(登录后单击“提交反馈”链接)。我不知道任何
数学成员都不会是线程安全的?Math
类的文档明确指出:“此类型的任何公共静态成员都是线程安全的。任何实例成员都不能保证是线程安全的。”对于非线程安全,我的意思是这是一个与多线程相结合的Bug。正如斯维克所说,你可能想报告。如果你需要一个快速的解决方案,你可以使用锁或者创建自己的Max方法,如我的帖子所示。有趣的是:Parallel.For(0,2000,Process)代码>只是偶尔会崩溃。我可以证明,这是一个与多线程相结合的Bug。所以我不把有缺陷的方法称为线程安全的。@MatthiasMüller什么是线程不安全的?仅因为在这种特定情况下,仅当将Math.Max()
与多线程一起使用时才会出现错误,并不意味着Math.Max()
不是线程安全的。
public void Add()
{
// use Min() instead of Max() -> bug disappears
myval.X = myval.X > 0 ? myval.X : 0;
}