首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++\Cli互锁::Add for doubles无法获得互锁::CompareExchange替代工作

C++\Cli互锁::Add for doubles无法获得互锁::CompareExchange替代工作
EN

Stack Overflow用户
提问于 2014-05-28 13:30:33
回答 1查看 286关注 0票数 0

这是对前面一个问题的后续,该问题解决了带线程局部变量并行::使用局部变量的并行::For语法。

我正在尝试实现的图书示例执行一个互锁的::Add on int,但是我的实现需要加倍。在上面引用的代码中,我实现了一个Lock例程,但这不起作用。因此,我现在正在尝试将一个C#示例转换为一个C++\Cli类,然后我可以调用这个类作为双重Interlocked.CompareExchange的安全添加。

TreadSafe.h

代码语言:javascript
复制
using namespace System;
using namespace System::Threading;

ref class ThreadSafe
{
private:
    double totalValue;

public:
    property double Total
    {
        double get()
        {
            return totalValue;
        }
    }
    double AddToTotal(double addend);

    // constructor
    ThreadSafe(void);
};

ThreadSafe.cpp

代码语言:javascript
复制
// constructor
ThreadSafe::ThreadSafe(void)
{
    totalValue = 0.0;
}

double ThreadSafe::AddToTotal(double addend)
{
    double initialValue, computedValue;
    do
    {
        initialValue = totalValue;
        computedValue = initialValue + addend;
    }
    while (initialValue != Interlocked::CompareExchange(totalValue, computedValue, initialValue));

    return computedValue;
}

然后,我调用包含并行:For例程的Class,该例程也在上面引用的post中列出。

DataCollection.cpp

代码语言:javascript
复制
// constructor
DataCollection::DataCollection(Form1^ f1) // takes parameter of type Form1 to give acces to variables on Form1
{
    this->f1 = f1;

    ts = gcnew ThreadSafe();
}

// initialize data set for parallel processing
void DataCollection::initNumbers(int cIdx, int gIdx)
{
    DataStructure^ number;
    numbers = gcnew List<DataStructure^>();

    for (int i = 0; i < f1->myGenome->nGenes; i++)
    {
// creates collection "numbers" with data to be processed in parallel
    }
}

// parallel-for summation of scores
double DataCollection::sumScore()
{
    Parallel::For<double>(0, numbers->Count, gcnew Func<double>(this, &DataCollection::initSumScore),
                                                gcnew Func<int, ParallelLoopState^, double, double>(this, &DataCollection::computeSumScore),
                                                gcnew Action<double>(this, &DataCollection::finalizeSumScore));
    return ts->Total;
}

// returns start value
double DataCollection::initSumScore()
{
    return 0.0;
}

// perform sequence alignment calculation
double DataCollection::computeSumScore(int k, ParallelLoopState^ status, double tempVal)
{
// calls several external methods, but for testing simplified to an assignment only

    tempVal += 1.0;

    return tempVal;
}

// locked addition
void DataCollection::finalizeSumScore(double tempVal)
{
    ts->AddToTotal(tempVal);
}

代码编译并运行,但不添加。它总是返回0。

因此,我假设我对C#示例的翻译/实现是不正确的。怎么了?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-05-28 16:49:03

我使用您的ThreadSafe类做了一个非常简单的例子。

代码语言:javascript
复制
ref class foo
{
public:
  ThreadSafe^ ts;
  foo() {ts = gcnew ThreadSafe();}
  double init() { return 0.0; }
  double add(int i, ParallelLoopState^, double d) { return d + 1.0; }
  void end(double d) { ts->AddToTotal(d); }
};

int main(array<System::String^>^args)
{
  foo^ f = gcnew foo();
  Parallel::For<double>(0,1000000,
    gcnew Func<double>(f, &foo::init),
    gcnew Func<int, ParallelLoopState^, double, double>(f, &foo::add),
    gcnew Action<double>(f, &f::end));
  Console::WriteLine(f->ts->Total);
}

我看到它使用1、2和4个任务线程运行。总是输出1000000 (在这种情况下)。

非常肯定,您在ThreadSafe类中没有犯错误。我就开始找别的地方了。

使用编写的代码,numbers->Count将等于0,这将导致Parallel::For永远不会执行。我想您刚才忽略了填充numbers的代码,但如果没有,这将是它无法工作的原因之一。

更新:下面是我打算在我的Parallel:For循环中做什么的解释:

每个任务从0开始。每次对Add的调用都会将1添加到任务的运行量中,然后,一旦任务完成,就会将其总计写入ThreadSafe类。

因此,与其调用AddToTotal 100次,我只对每个任务调用一次:

  • 1任务:调用AddToTotal的值为100。
  • 两个任务: AddToTotal调用的值约为50,之和为100。
  • 4个任务: AddToTotal调用的值约为25个,之和为100。

我的add()函数仍然是线程安全的,因为它不访问输入参数d以外的任何值,输入参数d是从init()返回的值(任务启动时),或者是来自该任务的前一个add()的值。

为了证明我的观点,我在我的Console::WriteLine(d)函数中添加了一个end(),并将计数提高到了1000000。我跑了很多次,最后的计数总是1000000。下面是一个仍然有效的异常例子: 467922,454959,77119。总计1000000。(我第一次看到3项任务)。

当然,现在我已经考虑到您关心的问题了,我只打了几次电话给AddToTotal,可能永远不会同时调用它。

下面是我要添加()和end()的更新:

代码语言:javascript
复制
double add(int i, ParallelLoopState^, double d) { return bar->AddToTotal(1.0); }
void end(double d) { Console::WriteLine(d); }

现在,所有的添加都是在任务中完成的,AddToTotal将被调用1000000次。end()将只输出每个任务的最终编号,这是在上次调用add()时从AddToTotal返回的。

我还接到1000000个电话。我确实得到了更多的任务,很可能是因为现在对AddToTotal的所有调用。

所以我同意。我的第一次尝试并不能很好地证明AddToTotal是线程安全的。现在,我想是的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23913034

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档