[英]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.