[英]Interlocked.Exchange<T> slower than Interlocked.CompareExchange<T>?
[英]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次,而是僅對每個任務調用一次:
我的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.