简体   繁体   English

C ++ \\ Cli Interlocked :: Add for doubles-无法获得Interlocked :: CompareExchange替代方法

[英]C++\Cli Interlocked::Add for doubles - can't get Interlocked::CompareExchange alternative working

This is a follow-on to an earlier question that solved the Parallel::For syntax with thread local variables Parallel::For with local variable 这是先前的问题的后续解决方案,该问题解决了带有线程局部变量的Parallel :: For语法带有局部变量的Parallel :: For

The book example I am trying to implement performs a Interlocked::Add on ints, yet my implementation requires doubles. 我尝试实现的书籍示例在整数上执行了Interlocked :: Add,但是我的实现需要加倍。 In the referenced code above I implemented a Lock routine, but that doesn't work. 在上面引用的代码中,我实现了Lock例程,但这不起作用。 So I am now trying to convert a C# example into a C++\\Cli class that I can then call as a safe addition of doubles Interlocked.CompareExchange 因此,我现在尝试将C#示例转换为C ++ \\ Cli类,然后我可以将其作为安全的Doubles Interlocked.CompareExchange加法来调用

TreadSafe.h TreadSafe.h

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 ThreadSafe.cpp

// 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;
}

This I then call in the Class that contains the Parallel:For routine, which is also listed in the referenced post above. 然后,我在包含Parallel:For例程的类中进行调用,该类也在上面引用的文章中列出。

DataCollection.cpp DataCollection.cpp

// 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);
}

The code compiles and runs, but it doesn't add. 该代码可以编译并运行,但不会添加。 It always returns 0. 它总是返回0。

So I assume my translation / implementation of the C# example is not correct. 因此,我假设我对C#示例的翻译/实现不正确。 What's wrong? 怎么了?

I made an extremely simple example using your ThreadSafe class. 我使用您的ThreadSafe类做了一个非常简单的示例。

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);
}

I've seen it run with 1, 2 and 4 task threads. 我已经看到它运行有1、2和4个任务线程。 Always outputs 1000000 (for this case). 始终输出1000000(在这种情况下)。

Pretty sure you didn't make a mistake in your ThreadSafe class. 可以肯定的是,您在ThreadSafe类中没有犯错。 I'd start looking elsewhere. 我将开始寻找其他地方。

With your code as written, numbers->Count will equal 0, which will cause the Parallel::For to never execute. 编写代码后, numbers->Count将等于0,这将导致Parallel::For永远不会执行。 I assume you've just omitted the code that fills up numbers , but if not, that would be one reason it wouldn't work. 我假设您只是省略了填充numbers的代码,但是如果没有,那将是它不起作用的原因之一。

Update: Here is an explanation of what I intended to do in my Parallel:For loop: 更新:这是我打算在Parallel:For循环中进行的解释:
Each task starts at 0. Each call to Add adds 1 to the running amount for the task, then, once the task is finished, it writes out its total to the ThreadSafe class. 每个任务都从0开始。每次调用Add将任务的运行量Add 1 ,然后,一旦任务完成,它将其总数写到ThreadSafe类中。

So, instead of calling AddToTotal 100 times, I'm only calling it once per task: 因此,我没有为每个任务调用AddToTotal 100次,而是仅对每个任务调用一次:

  • 1 task: AddToTotal called with a value of 100. 1个任务:调用值为100的AddToTotal。
  • 2 tasks: AddToTotal called with 2 values that are approximately 50, summing to 100. 2个任务:用大约50的2个值调用AddToTotal,总计为100。
  • 4 tasks: AddToTotal called with 4 values that are approximately 25, summing to 100. 4个任务:用大约25的4个值调用AddToTotal,总计为100。

My add() function is still thread-safe, because it isn't accessing any values other than the input parameter d , which is either the value returned from init() (when the task was started) or the value from the previous add() for that task. 我的add()函数仍然是线程安全的,因为它不访问输入参数d以外的任何值,输入参数d是从init()返回的值(任务启动时)或前一个add()的值add()完成该任务。

To prove my point, I added a Console::WriteLine(d) to my end() function and upped my count to 1000000. I ran it many times and my final count is always 1000000. Here's an abnormal example that still worked: 467922, 454959, 77119. Total 1000000. (first time I've seen 3 tasks). 为了证明我的观点,我在我的end()函数中添加了一个Console::WriteLine(d)并将其计数增加到1000000。我运行了多次,最终计数始终为1000000。这是一个仍然有效的异常示例:467922 ,454959、77119。总计1000000。(我第一次看到3个任务)。

Of course, now I've just thought about your concern that I'm only calling AddToTotal a few times and it may never be called simultaneously. 当然,现在我刚刚想到了您的问题,我只几次调用AddToTotal ,而可能永远不会同时调用它。

Here is my update to add() and end(): 这是我对add()和end()的更新:

double add(int i, ParallelLoopState^, double d) { return bar->AddToTotal(1.0); }
void end(double d) { Console::WriteLine(d); }

Now, all the adds are done in the tasks and AddToTotal will be called 1000000 times. 现在,所有添加都在任务中完成,并且AddToTotal将被调用1000000次。 end() will only output the final number for each task, returned from AddToTotal in the last call to add() . end()将仅输出每个任务的最终编号,该编号是在上一次对add()调用中从AddToTotal返回的。

I still get 1000000 calls. 我仍然收到1000000个电话。 I do get more tasks, likely because of all the calls to AddToTotal now. 我确实得到了更多的任务,可能是因为现在所有对AddToTotal的调用。

So I agree. 所以我同意。 My first attempt wasn't a good proof that AddToTotal was thread-safe. 我的第一次尝试并不能很好地证明AddToTotal是线程安全的。 Now, I expect that it is. 现在,我希望是这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM