【问题标题】:Calling a method in another thread after a thread is finished线程完成后调用另一个线程中的方法
【发布时间】:2019-05-15 15:59:47
【问题描述】:

我正在尝试并行化我的程序,但由于我对线程非常陌生,因此遇到了一些问题。

我有两个属于同一个类的方法。其中一种方法在 for 循环中进行一些计算并将结果推送到向量中,另一种方法 (runTheResult) 获取向量并使用获得的向量启动线程。我希望每次 runTheResult 完成一个结果时启动另一个线程来运行下一个获得的结果,同时将一次最大线程数限制为 4。

我的程序结构是这样的:

void runTheResult(vector<double>& u){

//process 'u' and launch a thread 


};

void method(){

for(...){

//calculate

    for(...){

    //put the calculations in vector<double>result

    };

    runTheResult(result); 

};

};

我在谷歌上搜索了很多,其中一个解决方案是维护一个消息队列。然而,这个问题是如果我实现一个队列,我将不得不在一个while循环中定期用另一个线程检查队列。如果我使用像while(true){//check for new messages if number of threads is less than five} 这样的while 循环,我会失去很多处理能力,如果我选择在不满足条件的情况下让循环进入睡眠状态,我会浪费处理能力。我在线程中运行的函数每个需要 2-5 秒,我必须处理大约 1k 到 50k 的函数,所以即使每个循环延迟一秒也很多。

是否可以在每次 runTheResult 完成时在另一个线程中运行 runTheResult?还是有更好的方法来做到这一点?

【问题讨论】:

  • 你可以让另一个线程等待一个条件变量并唤醒它,然后根据你将设置的一些标志执行你想要的函数。在 Windows 中,您还可以拥有一个消息队列并发布一条消息(不是您创建的 for 循环,而是 GetMessage())。对于简单的多线程计算,您还可以使用 future/promise。
  • 创建4个线程,让它们等待一个条件变量。每次您为其中一个线程准备好工作时,请致电notify_one。
  • @MichaelChourdakis 如果我检查标志,我仍然必须使用 while 循环,对吗?如果您不介意,您能详细说明一下吗?
  • 重点是,通过使用这些“等待”对象,for循环不会消耗系统资源。如果您在条件变量上等待,那么在等待时线程不会占用 CPU。您将执行一个 for 循环,但在其中您将检查条件变量,如果已设置,then 检查标志。
  • 在我的一篇较早的文章here中获得更多信息。

标签: c++ multithreading c++11 parallel-processing


【解决方案1】:

其他人告诉您使用消息队列,因为这是最安全的方法。您的程序必须至少有一个用户(您或最终用户)可以与之交互的主线程。只要您的程序运行,这个主线程就会一直循环。你在这里做你的消息处理

// this is not actually running the result now
// this only sends it to the main thread that will run the result
void runTheResult(vector<double>& u){ 

    //process 'u' and launch a thread. 
    // @NOTE Launching a thread again will not be beneficial as it will still be blocked 
    // by the mutex

    // convert/store vector into Message. To make it usable for other types
    // or you can just change Message to double
    Message u_message = to_message(u)

    std::lock_guard<std::mutex> lock(message_mutex);
    messages_shared.append(u_message);

};

void method() // runs on worker thread
{
    for(...){

    //put the calculations in vector<double>result

    };

    runTheResult(result);
}

void getMessages_safe(std::vector<Messages>& outMessages_safe)
{
    // as Ted Lyngo suggests, using lock_guard is best practice. See edit for alternative
    std::lock_guard<std::mutex> lock(message_mutex);
    outMessages_safe = messages_shared;
    messages_shared.clear();
}

std::vector<Message> messages_shared;
std::mutex message_mutex;

void main() { // this runs on the very first thread of the program
  while (isProgramRunning)
  {
      std::vector<Message> messages_safe; // safe to access by this thread only
      getMessages_safe(messages_safe);

      // dispatch messages to whoever needs it

      // launch worker thread
  }
}

【讨论】:

  • 为什么不对互斥锁使用适当的保护?如果messages_shared.append() 抛出,如果你像上面那样做,互斥锁将永远被锁定。 std::lock_guard&lt;std::mutex&gt; guard(message_mutex); messages_shared.append(u_message); 然后就不需要手动解锁了。看起来没有人在等待条件变量,所以至少有一个线程会像疯了一样旋转。
  • 确实如此。但因为这不是问题的主要话题。我使用了最明确的方式来使用互斥锁,因为我不知道操作是否知道它。
  • OP 似乎非常关心性能,因此当处理器可以做其他事情(比如处理操作系统)时,将处理器浪费在旋转上不会做得很好。此外,在处理线程时,锁守卫和条件变量几乎是必须的,所以我相信如果你把它们带入你的例子中,OP 会发现它们有什么好处。
  • 我已将答案编辑为使用lock_guard。但是我想不出一种方法来实现条件变量,而不使程序成为单个执行程序,以后无法启动新线程。
  • 好。关于condition_variables 当有人在messages_shared 中输入消息时,它可以notify_one 并且主线程可以等待而不是一遍又一遍地尝试获取不存在的消息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 2016-12-29
  • 2021-06-21
相关资源
最近更新 更多