我仍然是多线程的初学者,所以请耐心等待:
我目前正在编写一个在网格上进行FVM计算的应用程序。它是一个时间显式模型,因此在每个时间步都需要计算整个网格的新值。我的想法是将这个计算分配给4个工作线程,然后处理网格的单元格(第一个线程计算0,4,8 ......第二个线程1,5,9 ......等等)。 / p>
我在程序启动时创建了这4个线程。
他们看起来像这样:void __fastcall TCalculationThread::Execute()
{
bool alive = true;
THREAD_SIGNAL ts;
while (alive)
{
Sleep(1);
if (TryEnterCriticalSection(&TMS))
{
ts = thread_signal;
LeaveCriticalSection(&TMS);
alive = !ts.kill;
if (ts.go && !ts.done.at(this->index))
{
double delta_t = ts.dt;
for (unsigned int i=this->index; i < cells.size(); i+= this->steps)
{
calculate_one_cell();
}
EnterCriticalSection(&TMS);
thread_signal.done.at(this->index)=true;
LeaveCriticalSection(&TMS);
}
}
}
他们使用全局结构与主线程进行通信(当工作人员需要启动时,主线程将ts.go设置为true。
现在我确定这不是这样做的方法!不仅感觉不对,而且表现也不太好......
我读了例如here,信号量或事件会更好。 this guy's question的答案谈到无锁队列。
我对这些概念不是很熟悉,想要一些指针如何继续。 你可以列出任何改善这种方法的方法吗?
谢谢你的时间。 (并抱歉格式化)我使用的是borland c ++ builder及其thread-object(TThread)。
答案 0 :(得分:2)
绝对更有效的算法是在一个线程上计算0,1,2,3的收益率,在另一个线程上计算4,5,6,7的收益率等。这样的交错存储器访问非常糟糕,即使变量是完全独立的 - 你会得到错误的分享问题。这相当于CPU锁定每次写入。
答案 1 :(得分:2)
在计算线程中调用Sleep(1)
无法解决任何问题。你希望你的线程做有用的工作,而不是没有充分理由阻塞。
我认为您的基本问题可以表示为此基本形式的串行算法:
for (int i=0; i<N; i++)
cells[i]->Calculate();
你处于幸福的位置,呼叫Calculate()
是彼此独立的 - 你在这里所拥有的是平行的。这意味着您可以在没有互斥锁的情况下实现此功能。
有多种方法可以实现这一目标。 OpenMP将是一个;另一个是线程池类。如果要打开自己的基于线程的解决方案,则在共享变量上使用InterlockedIncrement()
来迭代数组。
你可能会遇到一些虚假的共享问题,正如@DeadMG建议的那样,但很可能不会。如果你确实有错误共享,那么另一种方法是跨越更大的子阵列。基本上,传递给InterlockedIncrement()
的增量(即步幅)将大于1。
最重要的是,使代码更快的方法是删除关键部分(以及因此对其的争用)和Sleep(1)
。