我已经将 boost 的一部分——ibeta_inv 函数——编译成一个 .Net 64 位程序集,在我开始从多线程调用它之前,它工作得很好。然后它偶尔会返回错误的结果。
我使用这段代码 (C++/CLI) 编译了它:
// Boost.h
#pragma once
#include <boost/math/special_functions/beta.hpp>
using namespace boost::math;
namespace Boost {
public ref class BoostMath
{
public:
double static InverseIncompleteBeta( double a, double b, double x )
{
return ibeta_inv(a,b,x);
}
};
}
有人试过吗?
我没有在 .Net 之外尝试过这个,所以我不知道这是否是原因,但我真的不明白为什么,因为它在单线程下工作得很好。
用法(C#):
private void calcBoost(List<Val> vals)
{
//gives WRONG results (sometimes):
vals.AsParallel().ForAll(v => v.BoostResult = BoostMath.InverseIncompleteBeta(v.A, v.B, v.X));
//gives CORRECT results:
vals.ForEach(v => v.BoostResult = BoostMath.InverseIncompleteBeta(v.A, v.B, v.X));
}
更新: 从我下面的评论中可以看出 - 我完全不确定这是一个 Boost 问题。也许这是一些奇怪的 PLinq to C++/CLI 错误???我很困惑,稍后会返回更多事实。
最佳答案
Val
类是线程安全的,这一点至关重要。
确保这一点的一个简单方法是使其不可变,但我看到您还需要编写 BoostResult
。所以这将需要 volatile
,或者有某种形式的锁定。
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;
}
}
}
锁定版本:(请参阅 this question 以确定最适合您的应用程序的版本)
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;
}
}
}
}
如果你认为 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();
}
}
}
关于c# - Boost 数学(ibeta_inv 函数)不是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9889127/