简体   繁体   English

如何在睡眠时唤醒 std::thread

[英]How to wake a std::thread while it is sleeping

I am using C++11 and I have a std::thread which is a class member, and it sends information to listeners every 2 minutes.我正在使用 C++11 并且我有一个std::thread ,它是一个类成员,它每 2 分钟向侦听器发送一次信息。 Other that that it just sleeps.其他的是它只是睡觉。 So, I have made it sleep for 2 minutes, then send the required info, and then sleep for 2 minutes again.所以,我让它休眠 2 分钟,然后发送所需的信息,然后再次休眠 2 分钟。

// MyClass.hpp
class MyClass {

    ~MyClass();
    RunMyThread();

private:
    std::thread my_thread;
    std::atomic<bool> m_running;
}


MyClass::RunMyThread() {

    my_thread = std::thread { [this, m_running] {
    m_running = true;
    while(m_running) {
        std::this_thread::sleep_for(std::chrono::minutes(2));
        SendStatusInfo(some_info);
    }
}};
}

// Destructor
~MyClass::MyClass() {
    m_running = false; // this wont work as the thread is sleeping. How to exit thread here?
}

Issue:问题:
The issue with this approach is that I cannot exit the thread while it is sleeping.这种方法的问题是我无法在线程休眠时退出它。 I understand from reading that I can wake it using a std::condition_variable and exit gracefully?我从阅读中了解到我可以使用std::condition_variable唤醒它并优雅地退出? But I am struggling to find a simple example which does the bare minimum as required in above scenario.但我正在努力寻找一个简单的例子,它可以满足上述场景中的要求。 All the condition_variable examples I've found look too complex for what I am trying to do here.我发现的所有condition_variable示例对于我在这里尝试做的事情来说都太复杂了。

Question:题:
How can I use a std::condition_variable to wake the thread and exit gracefully while it is sleeping?如何使用std::condition_variable唤醒线程并在线程休眠时正常退出? Or are there any other ways of achieving the same without the condition_variable technique?或者有没有其他方法可以在没有condition_variable技术的condition_variable实现相同的目标?

Additionally, I see that I need to use a std::mutex in conjunction with std::condition_variable ?此外,我发现我需要将std::mutexstd::condition_variable结合使用? Is that really necessary?真的有必要吗? Is it not possible to achieve the goal by adding the std::condition_variable logic only to required places in the code here?仅将std::condition_variable逻辑添加到此处代码中所需的位置是否无法实现目标?

Environment:环境:
Linux and Unix with compilers gcc and clang. Linux 和 Unix,带有 gcc 和 clang 编译器。

How can I use an std::condition_variable to wake the thread and exit gracefully while it was sleeping?如何使用std::condition_variable唤醒线程并在线程休眠时正常退出? Or are there any other ways of achieving the same without condition_variable technique?或者有没有其他方法可以在没有condition_variable技术的情况condition_variable实现相同的目标?

No, not in standard C++ as of C++17 (there are of course non-standard, platform-specific ways to do it, and it's likely some kind of semaphore will be added to C++2a).不,不是在 C++17 的标准 C++ 中(当然有非标准的、特定于平台的方法来做到这一点,并且可能会在 C++2a 中添加某种信号量)。

Additionally, I see that I need to use a std::mutex in conjunction with std::condition_variable ?此外,我发现我需要将std::mutexstd::condition_variable结合使用? Is that really necessary?真的有必要吗?

Yes.是的。

Is it not possible to achieve the goal by adding the std::condition_variable logic only to required places in the code piece here?仅将std::condition_variable逻辑添加到此处代码段中的所需位置是否无法实现目标?

No. For a start, you can't wait on a condition_variable without locking a mutex (and passing the lock object to the wait function) so you need to have a mutex present anyway.号一开始,你不能在等待condition_variable没有锁定一个互斥体(以及穿过锁定对象的等待功能),所以你需要有一个目前互斥反正。 Since you have to have a mutex anyway, requiring both the waiter and the notifier to use that mutex isn't such a big deal.由于无论如何您都必须有一个互斥锁,因此要求服务员和通知程序都使用该互斥锁并不是什么大问题。

Condition variables are subject to "spurious wake ups" which means they can stop waiting for no reason.条件变量会受到“虚假唤醒”的影响,这意味着它们可以无缘无故地停止等待。 In order to tell if it woke because it was notified, or woke spuriously, you need some state variable that is set by the notifying thread and read by the waiting thread.为了判断它是因为被通知而唤醒,还是虚假唤醒,您需要一些由通知线程设置并由等待线程读取的状态变量。 Because that variable is shared by multiple threads it needs to be accessed safely, which the mutex ensures.因为该变量由多个线程共享,所以需要安全访问它,互斥锁确保了这一点。

Even if you use an atomic variable for the share variable, you still typically need a mutex to avoid missed notifications.即使您使用原子变量作为共享变量,您通常仍然需要一个互斥锁来避免错过通知。

This is all explained in more detail in https://github.com/isocpp/CppCoreGuidelines/issues/554这在https://github.com/isocpp/CppCoreGuidelines/issues/554中有更详细的解释

How can I use an std::condition_variable to wake the thread and exit gracefully while it was sleeping?如何使用 std::condition_variable 唤醒线程并在线程休眠时正常退出?

You use std::condition_variable::wait_for() instead of std::this_thread::sleep_for() and first one can be interrupted by std::condition_variable::notify_one() or std::condition_variable::notify_all()您使用std::condition_variable::wait_for()而不是std::this_thread::sleep_for()并且第一个可以被std::condition_variable::notify_one()std::condition_variable::notify_all()

Additionally, I see that I need to use a std::mutex in conjunction with std::condition_variable?此外,我发现我需要将 std::mutex 与 std::condition_variable 结合使用? Is that really necessary?真的有必要吗? Is it not possible to achieve the goal by adding the std::condition_variable logic only to required places in the code piece here?仅将 std::condition_variable 逻辑添加到此处代码段中的所需位置是否无法实现目标?

Yes it is necessary to use std::mutex with std::condition_variable and you should use it instead of making your flag std::atomic as despite atomicity of flag itself you would have race condition in your code and you will notice that sometimes your sleeping thread would miss notification if you would not use mutex here.是的,有必要将std::mutexstd::condition_variable使用,您应该使用它而不是将标志设置为std::atomic因为尽管标志本身具有原子性,但您的代码中仍会存在竞争条件,并且您会注意到有时您的如果您不在这里使用互斥锁,睡眠线程将错过通知。

A working example for you using std::condition_variable :使用std::condition_variable工作示例:

struct MyClass {
    MyClass()
        : my_thread([this]() { this->thread(); })
    {}

    ~MyClass() {
        {
            std::lock_guard<std::mutex> l(m_);
            stop_ = true;
        }
        c_.notify_one();
        my_thread.join();
    }

    void thread() {
        while(this->wait_for(std::chrono::minutes(2)))
            SendStatusInfo(some_info);
    }

    // Returns false if stop_ == true.
    template<class Duration>
    bool wait_for(Duration duration) {
        std::unique_lock<std::mutex> l(m_);
        return !c_.wait_for(l, duration, [this]() { return stop_; });
    }

    std::condition_variable c_;
    std::mutex m_;
    bool stop_ = false;
    std::thread my_thread;
};

There is a sad, but true fact - what you are looking for is a signal, and Posix threads do not have a true signalling mechanism.有一个可悲但真实的事实 - 您正在寻找的是信号,而 Posix 线程没有真正的信号机制。

Also, the only Posix threading primitive associated with any sort of timing is conditional variable, this is why your online search lead you to it, and since C++ threading model is heavily built on Posix API, in standard C++ Posix-compatible primitives is all you get.此外,与任何类型的时序相关的唯一 Posix 线程原语是条件变量,这就是为什么您的在线搜索会引导您找到它,并且由于 C++ 线程模型大量构建在 Posix API 上,因此在标准 C++ Posix 兼容原语中,您就是全部得到。

Unless you are willing to go outside of Posix (you do not indicate platform, but there are native platform ways to work with events which are free from those limitations, notably eventfd in Linux) you will have to stick with condition variables and yes, working with condition variable requires a mutex, since it is built into API.除非你愿意离开 Posix(你没有指明平台,但有本地平台方式来处理不受这些限制的事件,特别是 Linux 中的eventfd )你将不得不坚持使用条件变量,是的,工作使用条件变量需要互斥锁,因为它内置于 API 中。

Your question doesn't specifically ask for code sample, so I am not providing any.您的问题没有特别要求提供代码示例,因此我不提供任何内容。 Let me know if you'd like some.如果你想要一些,请告诉我。

Additionally, I see that I need to use a std::mutex in conjunction with std::condition_variable?此外,我发现我需要将 std::mutex 与 std::condition_variable 结合使用? Is that really necessary?真的有必要吗? Is it not possible to achieve the goal by adding the std::condition_variable logic only to required places in the code piece here?仅将 std::condition_variable 逻辑添加到此处代码段中的所需位置是否无法实现目标?

std::condition_variable is a low level primitive. std::condition_variable是一个低级原语。 Actually using it requires fiddling with other low level primitives as well.实际上使用它也需要摆弄其他低级原语。

struct timed_waiter {
  void interrupt() {
    auto l = lock();
    interrupted = true;
    cv.notify_all();
  }
  // returns false if interrupted
  template<class Rep, class Period>
  bool wait_for( std::chrono::duration<Rep, Period> how_long ) const {
    auto l = lock();
    return !cv.wait_until( l,
      std::chrono::steady_clock::now() + how_long,
      [&]{
        return !interrupted;
      }
    );
  }
private:
  std::unique_lock<std::mutex> lock() const {
    return std::unique_lock<std::mutex>(m);
  }
  mutable std::mutex m;
  mutable std::condition_variable cv;
  bool interrupted = false;
};

simply create a timed_waiter somewhere both the thread(s) that wants to wait, and the code that wants to interrupt, can see it.只需在想要等待的线程和想要中断的代码都可以看到的地方创建一个timed_waiter

The waiting threads do等待线程做

while(m_timer.wait_for(std::chrono::minutes(2))) {
    SendStatusInfo(some_info);
}

to interrupt do m_timer.interrupt() (say in the dtor) then my_thread.join() to let it finish.中断做m_timer.interrupt() (在 dtor 中说)然后my_thread.join()让它完成。

Live example :现场示例

struct MyClass {
    ~MyClass();
    void RunMyThread();
private:
    std::thread my_thread;
    timed_waiter m_timer;
};


void MyClass::RunMyThread() {

    my_thread = std::thread {
      [this] {
      while(m_timer.wait_for(std::chrono::seconds(2))) {
        std::cout << "SendStatusInfo(some_info)\n";
      }
    }};
}

// Destructor
MyClass::~MyClass() {
    std::cout << "~MyClass::MyClass\n";
    m_timer.interrupt();
    my_thread.join();
    std::cout << "~MyClass::MyClass done\n";
}

int main() {
    std::cout << "start of main\n";
    {
        MyClass x;
        x.RunMyThread();
        using namespace std::literals;
        std::this_thread::sleep_for(11s);
    }
    std::cout << "end of main\n";
}

Or are there any other ways of achieving the same without the condition_variable technique?或者有没有其他方法可以在没有 condition_variable 技术的情况下实现相同的目标?

You can use std::promise / std::future as a simpler alternative to a bool / condition_variable / mutex in this case.在这种情况下,您可以使用std::promise / std::future作为bool / condition_variable / mutex的更简单替代方案。 A future is not susceptible to spurious wakes and doesn't require a mutex for synchronisation.一个future不容易受到虚假醒来后,不需要mutex同步。

Basic example:基本示例:

std::promise<void> pr;
std::thread thr{[fut = pr.get_future()]{
    while(true)
    {
        if(fut.wait_for(std::chrono::minutes(2)) != std::future_status::timeout)
            return;
    }
}};
//When ready to stop
pr.set_value();
thr.join();

Or are there any other ways of achieving the same without condition_variable technique?或者有没有其他方法可以在没有 condition_variable 技术的情况下实现相同的目标?

One alternative to a condition variable is you can wake your thread up at much more regular intervals to check the "running" flag and go back to sleep if it is not set and the allotted time has not yet expired:条件变量的一种替代方法是,您可以以更规律的时间间隔唤醒线程以检查“正在运行”标志,如果未设置且分配的时间尚未到期,则返回睡眠状态:

void periodically_call(std::atomic_bool& running, std::chrono::milliseconds wait_time)
{
    auto wake_up = std::chrono::steady_clock::now();

    while(running)
    {
        wake_up += wait_time; // next signal send time

        while(std::chrono::steady_clock::now() < wake_up)
        {
            if(!running)
                break;

            // sleep for just 1/10 sec (maximum)
            auto pre_wake_up = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);

            pre_wake_up = std::min(wake_up, pre_wake_up); // don't overshoot

            // keep going to sleep here until full time
            // has expired
            std::this_thread::sleep_until(pre_wake_up);
        }

        SendStatusInfo(some_info); // do the regular call
    }
}

Note: You can make the actual wait time anything you want.注意:您可以根据需要设置实际等待时间。 In this example I made it 100ms std::chrono::milliseconds(100) .在这个例子中,我把它设为 100ms std::chrono::milliseconds(100) It depends how responsive you want your thread to be to a signal to stop.这取决于您希望线程对停止信号的响应程度。

For example in one application I made that one whole second because I was happy for my application to wait a full second for all the threads to stop before it closed down on exit.例如,在一个应用程序中,我花了整整一秒,因为我很高兴我的应用程序在退出时关闭之前等待所有线程停止一整秒。

How responsive you need it to be is up to your application.您需要它的响应程度取决于您的应用程序。 The shorter the wake up times the more CPU it consumes.唤醒时间越短,它消耗的CPU就越多。 However even very short intervals of a few milliseconds will probably not register much in terms of CPU time.然而,即使是几毫秒的非常短的间隔也可能不会在CPU时间方面记录太多。

You could also use promise/future so that you don't need to bother with conditionnal and/or threads:您还可以使用 promise/future,这样您就无需担心条件和/或线程:

#include <future>
#include <iostream>

struct MyClass {

    ~MyClass() {
        _stop.set_value();
    }

    MyClass() {
        auto future = std::shared_future<void>(_stop.get_future());
        _thread_handle = std::async(std::launch::async, [future] () {
            std::future_status status;
            do {
                status = future.wait_for(std::chrono::seconds(2));
                if (status == std::future_status::timeout) {
                    std::cout << "do periodic things\n";
                } else if (status == std::future_status::ready) {
                    std::cout << "exiting\n";
                }
            } while (status != std::future_status::ready);
        });
    }


private:
    std::promise<void> _stop;
    std::future<void> _thread_handle;
};


// Destructor
int main() {
    MyClass c;
    std::this_thread::sleep_for(std::chrono::seconds(9));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM