C# 增强数学(ibeta_inv函数)不是线程安全的?

C# 增强数学(ibeta_inv函数)不是线程安全的?,c#,.net,boost,c++-cli,plinq,C#,.net,Boost,C++ Cli,Plinq,我已经将boost的一部分(ibeta_inv函数)编译成了一个.Net 64位程序集,在我开始从多个线程调用它之前,它工作得非常好。然后它会偶尔返回错误的结果 我使用以下代码编写了它(C++/CLI): //Boost.h #布拉格语一次 #包括 使用名称空间boost::math; 名称空间提升{ 公共引用类BoostMath { 公众: 双静态反转不完整(双a、双b、双x) { 返回ibeta_库存(a、b、x); } }; } 以前有人试过这个吗 我没有在.Net之外尝试过这个,所以我

我已经将boost的一部分(ibeta_inv函数)编译成了一个.Net 64位程序集,在我开始从多个线程调用它之前,它工作得非常好。然后它会偶尔返回错误的结果

我使用以下代码编写了它(C++/CLI):

//Boost.h
#布拉格语一次
#包括
使用名称空间boost::math;
名称空间提升{
公共引用类BoostMath
{
公众:
双静态反转不完整(双a、双b、双x)
{
返回ibeta_库存(a、b、x);
}
};
}
以前有人试过这个吗

我没有在.Net之外尝试过这个,所以我不知道这是否是原因,但我真的不明白为什么,因为它在单线程中工作得很好

用法(C#):

private void calcBoost(列出VAL)
{
//给出错误的结果(有时):
vals.AsParallel().ForAll(v=>v.BoostResult=BoostMath.inverseInCompleteTebeta(v.A,v.B,v.X));
//给出正确的结果:
vals.ForEach(v=>v.BoostResult=BoostMath.inverseInCompleteTebeta(v.A,v.B,v.X));
}

更新:正如我在下面的评论中所看到的,我再也不确定这是否是一个提升问题。也许这是一些奇怪的C++/CLI错误???我被吹嘘了,稍后会带着更多的事实回来。

重要的是
Val
类是线程安全的

确保这一点的一个简单方法是使其不可变,但我发现您还需要编写
BoostResult
。因此,它需要是易变的,或者具有某种形式的锁定

public sealed class Val
{
    // Immutable fields are inheriently threadsafe 
    public readonly double A;
    public readonly double B;
    public readonly double X;

    // volatile is an easy way to make a single field thread safe
    // box and unbox to allow double as volatile
    private volatile object boostResult = 0.0;

    public Val(double A, double B, double X)
    {
        this.A = A;
        this.B = B;
        this.X = X;
    }

    public double BoostResult
    {
        get
        {
            return (double)boostResult;
        }
        set
        {
            boostResult = value;
        }
    }
}
锁定版本:(请参阅以确定最适合您的应用程序的版本)

如果您认为600万个锁会很慢,请尝试以下方法:

using System;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            { //without locks
                var startTime = DateTime.Now;
                int i2=0;
                for (int i = 0; i < 6000000; i++)
                {
                    i2++;
                }
                Console.WriteLine(i2);
                Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.01 seconds on my machine
            }
            { //with locks
                var startTime = DateTime.Now;
                var obj = new Object();
                int i2=0;
                for (int i = 0; i < 6000000; i++)
                {
                    lock (obj)
                    {
                        i2++;
                    }
                }
                Console.WriteLine(i2);
                Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.14 seconds on my machine, and this isn't even in parallel.
            }
            Console.ReadLine();
        }
    }
}
使用系统;
命名空间控制台应用程序17
{
班级计划
{
静态void Main(字符串[]参数)
{
{//无锁
var startTime=DateTime.Now;
int i2=0;
对于(int i=0;i<6000000;i++)
{
i2++;
}
控制台写入线(i2);
Console.WriteLine(DateTime.Now.Subtract(startTime));//0.01秒在我的机器上
}
{//带锁
var startTime=DateTime.Now;
var obj=新对象();
int i2=0;
对于(int i=0;i<6000000;i++)
{
锁(obj)
{
i2++;
}
}
控制台写入线(i2);
在我的机器上,Console.WriteLine(DateTime.Now.Subtract(startTime));//0.14秒,这甚至不是并行的。
}
Console.ReadLine();
}
}
}

我恰巧将boost的一部分封装在一个C++/CLI 64位项目中,并从C#完全按照您的方式使用它

我把自己的C++类扔进了自己的Boost包装器,并把这个代码添加到了C项目中:

私有类Val
{
公共双A;
公共双B;
公共双X;
公共双并行结果;
}
私有静态void findParallelError()
{
var r=新的随机变量();
while(true)
{
var vals=新列表();
对于(变量i=0;i<1000*1000;i++)
{
var val=新的val();
val.A=r.NextDouble()*100;
val.B=val.A+r.NextDouble()*1000;
val.X=r.NextDouble();
val.Add(val);
}
//并行计算
vals.AsParallel().ForAll(v=>v.parallelResult=Boost.BoostMath.inverseInCompleteTebeta(v.A,v.B,v.X));
/顺序验证
var error=VAL.Exists(v=>v.parallelResult!=Boost.BoostMath.InverseIncompleteBeta(v.A,v.B,v.X));
如果(错误)
返回;
}
}
它只是执行“永远”。并行结果始终等于顺序结果。这里没有线程不安全

我可以建议您下载一个全新的Boost副本,并将其包含在一个全新的项目中,然后尝试一下吗


我还注意到您将结果称为“BoostResult”。。。并在评论中提到“我们目前的实施情况”。你到底在拿你的结果和别人比较什么?您对“correct”的定义是什么?

文档中说,只要您使用内置浮点类型,整个boost.math就应该是线程安全的(正如我看到的,您是这样做的)。也许你应该提交一个bug?如果没有其他的东西出现,我可以尝试在本地的C++应用程序中,看看问题是否仍然存在。如果是这样,一个bug报告可能是唯一要做的事情,因为我在源代码中找不到任何东西。可惜。。。它的运行速度是我们目前实现的反向不完全beta函数的两倍。很有趣!我想到了两个想法:(1)在多线程模式下构建boost的高音检查(当前版本仍有区别)和(2)这段引用,来自文档@stanwise linked:
后一个限制的原因是需要使用构造初始化符号常量。。。在这种情况下,需要运行T的构造函数,从而导致潜在的竞争条件。
我公开怀疑您的代码是否意外地暴露了这种竞争条件,我衷心支持stanwise将此报告为错误。让我们看看您的
Val
类。或者至少用一个不可变的
Val
类试试。@weston:你找到了什么!我的Val类只有5个双精度。没有别的了。然而,你的询问使我从类变成了stuct:瞧,当Val是一个struct时,我无法重现这个问题!!!然而,这提出了一系列新问题。我希望我能在一天内抽出时间更新这个问题,以反映我的新发现。不,这是不正确的。第一版-我仍然
public sealed class Val
{
    // Immutable fields are inheriently threadsafe 
    public readonly double A;
    public readonly double B;
    public readonly double X;

    // volatile is an easy way to make a single field thread safe
    // box and unbox to allow double as volatile
    private volatile object boostResult = 0.0;

    public Val(double A, double B, double X)
    {
        this.A = A;
        this.B = B;
        this.X = X;
    }

    public double BoostResult
    {
        get
        {
            return (double)boostResult;
        }
        set
        {
            boostResult = value;
        }
    }
}
public sealed class Val
{
    public readonly double A;
    public readonly double B;
    public readonly double X;

    private readonly object lockObject = new object();
    private double boostResult;

    public Val(double A, double B, double X)
    {
        this.A = A;
        this.B = B;
        this.X = X;
    }

    public double BoostResult
    {
        get
        {
            lock (lockObject)
            {
                return boostResult;
            }
        }
        set
        {
            lock (lockObject)
            {
                boostResult = value;
            }
        }
    }
}
using System;

namespace ConsoleApplication17
{
    class Program
    {
        static void Main(string[] args)
        {
            { //without locks
                var startTime = DateTime.Now;
                int i2=0;
                for (int i = 0; i < 6000000; i++)
                {
                    i2++;
                }
                Console.WriteLine(i2);
                Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.01 seconds on my machine
            }
            { //with locks
                var startTime = DateTime.Now;
                var obj = new Object();
                int i2=0;
                for (int i = 0; i < 6000000; i++)
                {
                    lock (obj)
                    {
                        i2++;
                    }
                }
                Console.WriteLine(i2);
                Console.WriteLine(DateTime.Now.Subtract(startTime)); //0.14 seconds on my machine, and this isn't even in parallel.
            }
            Console.ReadLine();
        }
    }
}
    private class Val
    {
        public double A;
        public double B;
        public double X;
        public double ParallellResult;
    }

    private static void findParallelError()
    {
        var r = new Random();

        while (true)
        {
            var vals = new List<Val>();
            for (var i = 0; i < 1000*1000; i++)
            {
                var val = new Val();
                val.A = r.NextDouble()*100;
                val.B = val.A + r.NextDouble()*1000;
                val.X = r.NextDouble();
                vals.Add(val);
            }

            // parallel calculation
            vals.AsParallel().ForAll(v => v.ParallellResult = Boost.BoostMath.InverseIncompleteBeta(v.A, v.B, v.X));

            /sequential verification
            var error = vals.Exists(v => v.ParallellResult != Boost.BoostMath.InverseIncompleteBeta(v.A, v.B, v.X));
            if (error)
                return;
        }
    }