簡體   English   中英

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

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

這是先前的問題的后續解決方案,該問題解決了帶有線程局部變量的Parallel :: For語法帶有局部變量的Parallel :: For

我嘗試實現的書籍示例在整數上執行了Interlocked :: Add,但是我的實現需要加倍。 在上面引用的代碼中,我實現了Lock例程,但這不起作用。 因此,我現在嘗試將C#示例轉換為C ++ \\ Cli類,然后我可以將其作為安全的Doubles Interlocked.CompareExchange加法來調用

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

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

然后,我在包含Parallel:For例程的類中進行調用,該類也在上面引用的文章中列出。

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

該代碼可以編譯並運行,但不會添加。 它總是返回0。

因此,我假設我對C#示例的翻譯/實現不正確。 怎么了?

我使用您的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);
}

我已經看到它運行有1、2和4個任務線程。 始終輸出1000000(在這種情況下)。

可以肯定的是,您在ThreadSafe類中沒有犯錯。 我將開始尋找其他地方。

編寫代碼后, numbers->Count將等於0,這將導致Parallel::For永遠不會執行。 我假設您只是省略了填充numbers的代碼,但是如果沒有,那將是它不起作用的原因之一。

更新:這是我打算在Parallel:For循環中進行的解釋:
每個任務都從0開始。每次調用Add將任務的運行量Add 1 ,然后,一旦任務完成,它將其總數寫到ThreadSafe類中。

因此,我沒有為每個任務調用AddToTotal 100次,而是僅對每個任務調用一次:

  • 1個任務:調用值為100的AddToTotal。
  • 2個任務:用大約50的2個值調用AddToTotal,總計為100。
  • 4個任務:用大約25的4個值調用AddToTotal,總計為100。

我的add()函數仍然是線程安全的,因為它不訪問輸入參數d以外的任何值,輸入參數d是從init()返回的值(任務啟動時)或前一個add()的值add()完成該任務。

為了證明我的觀點,我在我的end()函數中添加了一個Console::WriteLine(d)並將其計數增加到1000000。我運行了多次,最終計數始終為1000000。這是一個仍然有效的異常示例:467922 ,454959、77119。總計1000000。(我第一次看到3個任務)。

當然,現在我剛剛想到了您的問題,我只幾次調用AddToTotal ,而可能永遠不會同時調用它。

這是我對add()和end()的更新:

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是線程安全的。 現在,我希望是這樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM