在c++中用信号改变std::线程的执行流程

Change std::thread execution flow with signals in c++

本文关键字:线程 执行流 std 改变 c++ 信号      更新时间:2023-10-16

我有一个程序启动一个std::线程,做以下事情:sleep X,执行一个函数,终止。

create std::thread(Xms, &func)
  wait Xms
  then do func()
  end

我想知道我是否可以例如向我的std::线程发送一个信号,以便立即打破睡眠并执行func,然后退出。

我需要发送信号到std::thread::id来执行这个吗?

我的线程是这样启动的,使用lambda函数:
template<typename T, typename U>
void                          execAfter(T func, U params, const int ms)
{
  std::thread                 thread([=](){
      std::this_thread::sleep_for(std::chrono::milliseconds(ms));
      func(params);
    });
  thread.detach();
}

如果线程模型不能更改,使用std::condition_variablewait_for将是可行的方法。在下面的代码片段中,condition_variable的使用被包装到一个类中,该类的对象必须在线程之间共享。

#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>
class BlockCondition
{
    private:
        mutable std::mutex m;
        std::atomic<bool> done;
        mutable std::condition_variable cv;
    public:
        BlockCondition()
        :
           m(),
           done(false),
           cv()
        {
        }
        void wait_for(int duration_ms)
        {
            std::unique_lock<std::mutex> l(m);
            int ms_waited(0);
            while ( !done.load() && ms_waited < duration_ms )
            {
               auto t_0(std::chrono::high_resolution_clock::now());
               cv.wait_for(l, std::chrono::milliseconds(duration_ms - ms_waited));
               auto t_1(std::chrono::high_resolution_clock::now());
               ms_waited += std::chrono::duration_cast<std::chrono::milliseconds>(t_1 - t_0).count();
            }
        }
        void release()
        {
            std::lock_guard<std::mutex> l(m);
            done.store(true);
            cv.notify_one();
        }
};
void delayed_func(BlockCondition* block)
{
    block->wait_for(1000);
    std::cout << "Hello actual workn";
}
void abortSleepyFunction(BlockCondition* block)
{
    block->release();
}
void test_aborted()
{
    BlockCondition b();
    std::thread delayed_thread(delayed_func, &b);
    abortSleepyFunction(&b);
    delayed_thread.join();
}
void test_unaborted()
{
    BlockCondition b();
    std::thread delayed_thread(delayed_func, &b);
    delayed_thread.join();
}
int main()
{
    test_aborted();
    test_unaborted();
}
请注意,可能是虚假的唤醒,会过早地中止wait调用。为了说明这一点,我们计算实际等待的毫秒数,并继续等待,直到设置完成标志。

正如在评论中指出的那样,这并不是解决问题的最明智的方法。由于实现适当的中断机制非常复杂,而且非常容易出错,因此这里有一些解决方法的建议:

不要在整个超时时间内休眠,而是简单地循环一个固定的小长度的休眠(例如10毫秒),直到所需的持续时间过去。每次睡眠后,检查原子标志是否请求中断。这是一个肮脏的解决方案,但却是最快的。

或者,为每个线程提供condition_variable并对其执行wait而不是执行this_thread::sleep。通知条件变量以指示中断请求。您可能仍然需要一个额外的标志来防止虚假的唤醒,这样您就不会意外地过早返回。

好的,为了弄清楚这个问题,我发现了一个新的实现,它受到了你们所有答案的启发,所以非常感谢。

首先我要在主游戏道具中添加一个BombHandler道具。它将有一个包含所有Bomb项的属性。

这个BombHandler将是一个单例,包含一个timerLoop()函数,它将在一个线程中执行(这样我只使用一个线程来处理xxx个炸弹,更有效)

timerLoop()将ussleep(50),然后遍历整个std::list元素并调用Bomb::incrTimer(),后者将其内部_timer属性无限地增加10ms,并检查必须爆炸的炸弹。

例如,当它们达到2000ms时,bombhandler . explosion()将被调用,引爆炸弹并删除它。

如果另一个炸弹在范围内,炸弹::touchByFire()将被调用,并设置炸弹的内部属性,_timer,为time_to_explosion (1950ms)。

那么它将在50ms后被BombHandler:: explosion()引爆。

这不是一个很好的解决方案吗?

再次感谢你的回答!希望这能帮到你。