c# - Boost 数学(ibeta_inv 函数)不是线程安全的?

标签 c# .net boost c++-cli plinq

我已经将 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/

相关文章:

c# - 如何在 C# 中追加 xml 文件?

c# - 在 Dictionary<string, Dictionary<string, int>> 上进行 linq 查询时出现问题

c++ - Boost.Spirit气值序列 vector

c++ - 通过 boost 几何从多边形(环)中裁剪多边形(环)的一部分

c++ - 与小代码的结果相比,boost 的字节序检查不同

c# - 哪些程序集在 "found conflict between different versions"中发生冲突?

c# - ComboBox SelectedItem 与 SelectedValue

c# - 使用 IExcelDataReader 从 excel 中获取正确的文本显示

c# - IIS 中单个网站配置的 ASP.net 应用程序的多个实例

.net - 在 ASP.net 中一个页面只能有一个服务器端 Form 标签