我如何正确设计工人线程? (避免例如睡眠(1))

时间:2011-07-04 20:02:02

标签: c++ multithreading c++builder

我仍然是多线程的初学者,所以请耐心等待:

我目前正在编写一个在网格上进行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)。

2 个答案:

答案 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)