【问题标题】:How to make a thread wait for a condition to perform an operation without it using too much CPU time?如何让线程等待条件执行操作而不使用过多的 CPU 时间?
【发布时间】:2020-07-21 20:34:49
【问题描述】:

我一直在尝试在矩阵运算中使用线程,以使其更快地处理大型矩阵 (1000x1000)。到目前为止,我已经用下面的代码取得了一些成功。与使用单线程相比,速度显着提高。

void updateG(Matrix &u, Matrix &g, int n, int bgx, int tamx, int tamy) 
{
int i, j;

for (i = bgx; i < tamx; i += n)
 {
    for (j = 0; j < tamy; j++)
    {
        g(i,j, g(i,j)+ dt * 0.5 * (u(i,j) - (g(i,j) * y)));
    }
 }
}
void updateGt(Matrix &u, Matrix &g, int tam)strong text
{
 int i;
 const int n = NT;
 std::thread array[n];
 for (int i = 0; i < n; i++)
 {
    array[i] = std::thread(updateG, std::ref(u), std::ref(g), n, i, tam, tam);
 }
 joinAll(array, n);
}

但是,我需要在主代码中多次调用这个操作,而且每次出现这种情况都必须重新初始化线程数组,创建新线程,浪费很多时间(根据我在网上看到的那些很贵)。 因此,我开发了一种替代解决方案,只创建和初始化线程数组一次,并在每次调用函数时使用相同的线程来执行矩阵运算。使用一些标志变量,以便线程仅在必要时执行操作。就像下面的代码:

  void updateG(int bgx,int tam)
  {
    while (!flaguGkill[bgx]) {
        if (flaguG[bgx]) {
            int i, j;

            for (i = bgx; i < tam; i += NT)
            {
                for (j = 0; j < tam; j++)
                {
                    g->operator()(i, j, g->operator()(i, j) + dt * 0.5 * (u->operator()(i, j) - (g->operator()(i, j) * y)));
                }
            }
            flaguG[bgx] = false;

        }
    }
  }

 void updateGt()
 {
    
    for (int k = 0; k < NT; k++)
    {
        flaguG[k] = true;

    }

    for (int i = 0; i < NT; i++)
    {
        while(flaguG[i]);
    }
   }

我的问题是。这个应该更快的解决方案比第一个解决方案要慢得多,幅度很大。在我的完整代码中,我有 2 个函数,例如 updateGt 和 updateXt,每个函数使用 4 个线程,我认为问题在于,虽然该函数应该处于空闲等待状态,但它却占用了大量 CPU 时间只是为了继续检查代码。任何人都知道是否真的是这样,如果是这样,我该如何解决?

【问题讨论】:

  • 您必须使用std::mutexstd::condition_variable。在当前状态下,显示的代码是未定义的行为,它会严重崩溃只是时间问题。
  • 为什么不改用 OpenMP?
  • @mpoeter 以前没研究过,这是我第一次用线程做实验,因为我刚刚读到它是一个用更简单的方式实现并发的api,对吧?如果是这样,如果这种方法根本不起作用,我可能会尝试下一个。
  • @AlanBirtles SamVarshavchik 感谢您的提示,我将阅读这些主题。令人惊讶的是它没有中断,并且输出结果与其他版本的代码相同,只是速度慢了很多。

标签: c++ multithreading


【解决方案1】:

这里的问题叫做busy waiting。正如 cmets 中提到的,您需要使用 std::condition_variable,如下所示:

std::mutex mutex;
std::condition_variable cv;   

while (!flaguGkill[bgx]) {
    {
    unique_lock<mutex> lock(mutex); // aquire mutex lock as required by condition variable
    cv.wait(lock, [this]{return flaguG[bgx];}); // thread will suspend here and release the lock if the expression does not return true
    } 

    int i, j;
    
    for (i = bgx; i < tam; i += NT)
    {
        for (j = 0; j < tam; j++)
        {
            g->operator()(i, j, g->operator()(i, j) + dt * 0.5 * (u->operator()(i, j) - (g->operator()(i, j) * y)));
        }
    }
    flaguG[bgx] = false;
    
}

注意:[this] { return flaguG[bgx];} 部分,您可能需要根据这些变量的范围更改捕获参数([] 中的位)

如果您将其设置为 true,则需要调用

for (int k = 0; k < NT; k++)
{
    flaguG[k] = true;
    cv.notify_one();
}

// you can then use another condition variable here

【讨论】:

  • 谢谢,这证明是有效的!我不知道为什么,它仍然比第一个代码慢得多,考虑到其他一切都相等,是不是应该如此?还是我做错了什么?
  • 它可能锁定互斥锁的时间超过了使您的应用程序本质上是单线程所需的时间
  • 是的,抱歉,您可能想更改互斥锁的持有时间(它是RAII,因此您可以向上移动范围括号)。
  • 就是这样!等待修复后显式解锁互斥锁!谢谢大家!
  • 很高兴听到,我很傻,一开始不这样做。答案已更新,以便对其他人有用
猜你喜欢
  • 2021-05-14
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
  • 2018-09-29
  • 2013-01-17
  • 2021-07-18
  • 1970-01-01
  • 2015-02-22
相关资源
最近更新 更多