【问题标题】:Wake up a std::thread from usleep从睡眠中唤醒一个 std::thread
【发布时间】:2015-11-20 20:53:25
【问题描述】:

考虑以下示例:

#include <iostream>
#include <fstream>
#include <unistd.h>

#include <signal.h>
#include <thread>

void sleepy() {
    usleep(1.0E15);
}

int main() {
    std :: thread sleepy_thread(sleepy);

    // Wake it up somehow...?

    sleepy_thread.join();
}

这里我们有一个永远休眠的线程。我想加入它,而不必永远等待它自发地从睡梦中醒来。有没有办法从外部“嘿,伙计,醒醒!”告诉它,这样我就可以在合理的时间内加入它?

我绝对不是线程专家,所以如果可能的话,不要假设任何事情。

【问题讨论】:

  • IIRC usleep(1.0E15); 没有 floatdouble 值??
  • @πάνταῥεῖ:那又怎样? 1e15 可隐式转换为 int。虽然这有点儿 int.
  • @LightnessRacesinOrbit “虽然int 是我的观点。无论如何,这是一个很大的价值。
  • @OP 另外你应该注意,usleep()actually deprecated
  • 给它发个信号怎么样?

标签: c++ multithreading


【解决方案1】:

不,不能使用标准库中的线程。

一种可能的解决方法是使用 condition_variable::sleep_for 以及 mutex 和布尔条件。

#include <mutex>
#include <thread>
#include <condition_variable>

std::mutex mymutex;
std::condition_variable mycond;
bool flag = false;

void sleepy() {
     std::unique_lock<std::mutex> lock(mymutex);
     mycond.wait_for( lock,
                      std::chrono::seconds(1000),
                      []() { return flag; } );
}

int main()
{
    std :: thread sleepy_thread(sleepy);

    {
       std::lock_guard<std::mutex> lock(mymutex);
       flag = true;
       mycond.notify_one();
    }

    sleepy_thread.join();
}

或者,您可以使用实现中断点概念的Boost.Thread库:

#include <boost/thread/thread.hpp>

void sleepy()
{
    // this_thread::sleep_for is an interruption point.
    boost::this_thread::sleep_for( boost::chrono::seconds(1000) );
}

int main()
{
    boost::thread t( sleepy );

    t.interrupt();
    t.join();

}

【讨论】:

    【解决方案2】:

    其他答案是说您可以使用定时静音来完成此操作。我已经使用定时互斥锁组合了一个小类来阻止“睡眠”线程,如果您想早点“唤醒”它们,则释放互斥锁。标准库为timed_mutex 提供了一个名为try_lock_for 的函数,该函数将尝试锁定互斥体一段时间,然后继续(并返回失败指示)

    这可以封装在一个类中,就像下面的实现一样,它只允许一次调用来唤醒等待线程。还可以通过包含一个waitUntil 函数来改进它,该函数等待一个时间序列对应于timed_mutex 的另一个定时等待函数try_lock_until,但我将把它作为一个练习留给感兴趣的人,因为它看起来一个简单的修改。


    #include <iostream>
    #include <mutex>
    #include <thread>
    #include <chrono>
    #include <atomic>
    
    // one use wakable sleeping class
    class InterruptableSleeper{
        std::timed_mutex
            mut_;
        std::atomic_bool
            locked_; // track whether the mutex is locked
        void lock(){ // lock mutex
            mut_.lock();
            locked_ = true;
        }
        void unlock(){ // unlock mutex
            locked_ = false;
            mut_.unlock();
        }
    public:
        // lock on creation
        InterruptableSleeper() {
            lock();
        }
        // unlock on destruction, if wake was never called
        ~InterruptableSleeper(){
            if(locked_){
                unlock();
            }
        }
        // called by any thread except the creator
        // waits until wake is called or the specified time passes
        template< class Rep, class Period >
        void sleepFor(const std::chrono::duration<Rep,Period>& timeout_duration){
            if(mut_.try_lock_for(timeout_duration)){
                // if successfully locked, 
                // remove the lock
                mut_.unlock();
            }
        }
        // unblock any waiting threads, handling a situation
        // where wake has already been called.
        // should only be called by the creating thread
        void wake(){
            if(locked_){
                unlock();
            }
        }
    };
    

    以下代码:

    void printTimeWaited(
      InterruptableSleeper& sleeper, 
      const std::chrono::milliseconds& duration){
        auto start = std::chrono::steady_clock::now();
        std::cout << "Started sleep...";
        sleeper.sleepFor(duration);
        auto end = std::chrono::steady_clock::now();
        std::cout 
            << "Ended sleep after "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
            << "ms.\n";
    }
    
    void compareTimes(unsigned int sleep, unsigned int waker){
        std::cout << "Begin test: sleep for " << sleep << "ms, wakeup at " << waker << "ms\n";
        InterruptableSleeper
            sleeper;
        std::thread
            sleepy(&printTimeWaited, std::ref(sleeper), std::chrono::milliseconds{sleep});
        std::this_thread::sleep_for(std::chrono::milliseconds{waker});
        sleeper.wake();
        sleepy.join();
        std::cout << "End test\n";
    }
    
    int main(){
    
        compareTimes(1000, 50);
        compareTimes(50, 1000);
    
    }
    

    打印

    Begin test: sleep for 1000ms, wakeup at 50ms
    Started sleep...Ended sleep after 50ms.
    End test
    Begin test: sleep for 50ms, wakeup at 1000ms
    Started sleep...Ended sleep after 50ms.
    End test
    
    示例和使用Coliru

    【讨论】:

      【解决方案3】:

      “有没有办法从外部“嘿,伙计,醒醒!”中分辨出来,这样我就可以在合理的时间内加入它?”

      不,根据 c++ 标准机制没有办法这样做。

      好吧,为了让你的线程被唤醒,你需要一种让其他线程控制它的机制。此外usleep() 是一个已弃用的 POSIX 函数:

      问题 6

      更新了说明以避免在应用程序要求中使用“必须”一词。

      此功能已过时。

      应用 IEEE Std 1003.1-2001/Cor 2-2004,项目 XSH/TC2/D6/144,将说明从“进程”信号掩码更新为“线程的信号掩码”,并添加声明 @987654326 @函数不需要是可重入的。

      您无法控制另一个线程,它将调用该函数。
      任何其他 sleep() 函数也是如此,即使是从 std::thread 声明的。

      正如其他答案或 cmets 中所述,您需要使用定时同步机制,例如来自线程函数的 std::timed_mutexstd::condition_variable

      【讨论】:

        【解决方案4】:

        只需使用信号量,调用sem_timedwait而不是usleep,并在调用join之前调用sem_post

        【讨论】:

        • 我使用 pthread 的回答被否决了。这也使用了一个 pthread 库。
        • 信号量实际上是c++标准吗?我能想到的最好的代理是std::condition_variable
        • 我其实很喜欢这个主意...!为什么这被否决了?
        • 例子中使用的是Linux特有的usleep,这里为什么不能使用Linux特有的信号量呢?
        【解决方案5】:

        一种可能的方法:(有很多方法可以完成......在你的线程中使用睡眠也不是一个好主意)

        ///Define a mutex
        void sleepy()
        {
            //try to take mutex lock which this thread will get if main thread leaves that
            //usleep(1.0E15);
        }
        
        
        int main()
        {
            //Init the Mutex
            //take mutex lock
            std :: thread sleepy_thread(sleepy);
        
            //Do your work
            //unlock the mutex...This will enable the sleepy thread to run
            sleepy_thread.join();
        }
        

        【讨论】:

        • “在你的线程中使用睡眠不是个好主意”那为什么呢?
        • 你应该充实你答案的std::mutex部分。
        【解决方案6】:

        睡一小会儿,看看变量是否发生了变化。

        #include <atomic>
        #include <unistd.h>
        #include <thread>
        
        std::atomic<int> sharedVar(1);
        void sleepy()
        {
            while (sharedVar.load())
            {
                usleep(500);
            }
        }
        
        int main()
        {
            std :: thread sleepy_thread(sleepy);
            // wake up
            sharedVar.store(0);
        }
        

        【讨论】:

        • 这段代码是非法的,因为对sharedVar的两次访问冲突,根据C++标准。
        • 不可能是非法的。你的意思是未定义的行为?如果 sleepy 在一个循环中读取旧版本的 sharedVar,它只会在一个循环后读取新值。
        • 问题不在于困倦可能会阅读旧版本。问题是任何事情都可能发生。
        • 请至少std::atomic&lt;int&gt; sharedVar = 1;
        • @πάνταῥεῖ:最好不要,因为那不会编译。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-04
        • 2012-10-08
        • 2016-02-03
        • 1970-01-01
        • 2014-07-29
        相关资源
        最近更新 更多