如何唤醒正在睡觉的pthread? 我正在用C++编写一个程序。我注意到它正在获得一些线程,它们的目的是每隔一段时间做一些事情,其中有3到4个线程。我决定通过编写一个调度程序服务进行重构,其他使用这些线程的地方可以订阅该服务,这将使我在任何时候运行的额外事件线程的数量减少到一个

如何唤醒正在睡觉的pthread? 我正在用C++编写一个程序。我注意到它正在获得一些线程,它们的目的是每隔一段时间做一些事情,其中有3到4个线程。我决定通过编写一个调度程序服务进行重构,其他使用这些线程的地方可以订阅该服务,这将使我在任何时候运行的额外事件线程的数量减少到一个,c++,linux,pthreads,sleep,C++,Linux,Pthreads,Sleep,我还没有任何代码使用它;在我开始写之前,我想知道这是否可行,并获得一些关于我设计的反馈。我想完成的简要描述如下: 添加事件 调用者提供事件和时间表 计划提供事件的下一次发生 (事件、计划)对添加到事件队列 中断睡眠事件线程(即唤醒它) 事件线程主循环 尝试获取事件队列中的下一个事件 如果没有未决事件,则直接转到4 获取下一个事件应该发生的时间 睡眠到下一个事件(如果没有等待事件,则永远睡眠) 如果睡眠因任何原因被中断,请返回1 如果睡眠成功完成,则执行当前事件 更新队列(删除事件,如果是重复事件

我还没有任何代码使用它;在我开始写之前,我想知道这是否可行,并获得一些关于我设计的反馈。我想完成的简要描述如下:

添加事件

  • 调用者提供事件和时间表
  • 计划提供事件的下一次发生
  • (事件、计划)对添加到事件队列
  • 中断睡眠事件线程(即唤醒它)
  • 事件线程主循环

  • 尝试获取事件队列中的下一个事件
  • 如果没有未决事件,则直接转到4
  • 获取下一个事件应该发生的时间
  • 睡眠到下一个事件(如果没有等待事件,则永远睡眠)
  • 如果睡眠因任何原因被中断,请返回1
  • 如果睡眠成功完成,则执行当前事件
  • 更新队列(删除事件,如果是重复事件,则重新插入)
  • 跳回1
  • 我做了一些研究,知道有可能中断睡眠线程,我相信只要阻止同时访问事件队列,就不会有任何危险行为。我认为唤醒线程必须是可能的,java的thread的sleep()调用在某些情况下会抛出InterruptedException,除非它不依赖于操作系统的底层睡眠调用,否则无论如何它必须是可能的

    问题: 有人能评论我的方法吗?这是一个轮子,我最好不要重新发明?具体来说,您如何中断休眠线程,以便在下一条指令时继续执行,并且可以从中断的线程中检测到这一点

    关于boost的说明

    我敢打赌,你可以用boost编写一个调度器,但是它编译并运行在一台机器上,因为没有更好的短语,这是一堆废话。我以前在它上面编译过boost程序,而每个拉入boost的文件通常需要30秒以上的时间来编译。如果我能避免这个恼人的发展障碍,我非常愿意

    附录-工作规范[根据caf的建议修订] 这就是我编写的代码。它已经过初步测试,但是已经正确地处理了单个和重复事件以及不同的延迟

    以下是事件线程的主体:

    void Scheduler::RunEventLoop()
    {
        QueueLock();                   // lock around queue access
        while (threadrunning)
        {
            SleepUntilNextEvent();     // wait for something to happen
    
            while (!eventqueue.empty() && e.Due())
            {                          // while pending due events exist
                Event e = eventqueue.top();
                eventqueue.pop();
    
                QueueUnlock();         // unlock
                e.DoEvent();           // perform the event
                QueueLock();           // lock around queue access
    
                e.Next();              // decrement repeat counter
                                       // reschedule event if necessary
                if (e.ShouldReschedule()) eventqueue.push(e);
            }
        }
        QueueUnlock();                 // unlock
        return;                        // if threadrunning is set to false, exit
    }
    
    以下是睡眠功能:

    void Scheduler::SleepUntilNextEvent()
    {
        bool empty = eventqueue.empty();  // check if empty
    
        if (empty)
        {
            pthread_cond_wait(&eventclock, &queuelock); // wait forever if empty
        }
        else
        {
            timespec t =                  // get absolute time of wakeup
                Bottime::GetMillisAsTimespec(eventqueue.top().Countdown() + 
                                             Bottime::GetCurrentTimeMillis());
            pthread_cond_timedwait(&eventclock, &queuelock, &t); // sleep until event
        }
    }
    
    最后,增编:

    void Scheduler::AddEvent(Event e)
    {
        QueueLock();
        eventqueue.push(e);
        QueueUnlock();
        NotifyEventThread();
    }
    
    相关变量声明:

    bool threadrunning;
    priority_queue<Event, vector<Event>, greater<Event> > eventqueue;
    pthread_mutex_t queuelock; // QueueLock and QueueUnlock operate on this
    pthread_cond_t eventclock;
    
    bool线程运行;
    优先级队列事件队列;
    pthread\u mutex\u t queuelock;//QueueLock和QueueUnlock在此基础上操作
    pthread_cond_t eventclock;
    

    为了处理泛型事件的问题,每个
    事件
    都包含一个指向抽象类型
    action
    的对象的指针,whos子类覆盖
    action::DoEvent
    。此方法是从内部调用的
    Event::DoEvent
    <代码>操作由其事件“拥有”,即,如果事件不再需要重新调度,它们将自动删除。

    您要查找的是
    pthread\u cond\u t
    对象、
    pthread\u cond\u timedwait
    pthread\u cond\u wait
    函数。您可以创建条件变量isThereAnyTaskToDo并在事件线程中等待它。添加新事件时,您只需使用
    pthread\u cond\u signal()

    唤醒事件线程即可在*NIX平台和Windows上实现多种可能性。计时器线程应该使用事件/条件变量对象上的某种定时等待来等待。在POSIX平台上,您可以使用。在Windows上,您可以选择计算必要的时间增量并在事件句柄上使用
    WaitForSingleObject()
    ,也可以将事件对象与或组合使用。Boost也有一些同步原语,您可以使用它们来实现类似于POSIX的原语,但可以移植

    更新:

    POSIX也有一些计时器功能,请参见我同意的,并且可以使用--
    pthread\u cond\u timedwait()
    来实现您所追求的行为。我只想补充一点,您可以简化事件线程主循环:

  • 尝试获取事件队列中的下一个事件
  • 如果没有未决事件,则直接转到4
  • 获取下一个事件应该发生的时间
  • 使用
    pthread\u cond\u timedwait()
    等待条件变量,直到下一个事件(如果没有计划的事件,则使用
    pthread\u cond\u Wait()
    等待)
  • 尝试获取事件队列中的下一个事件
  • 如果没有过期的事件,请返回4
  • 更新队列(删除事件,如果是重复事件,则重新插入)
  • 跳回到5
  • 所以你不在乎为什么醒来——无论何时醒来,你都会检查当前时间并运行任何已过期的事件,然后返回等待。在大多数情况下,当添加新事件时,您会发现没有任何事件已过期,当然,您只需重新计算等待时间


    您可能希望将队列实现为优先级队列,以便下一个过期事件始终位于最前面。

    您当前的解决方案包含竞争条件,例如:

    QueueLock();                      // lock around queue access
    bool empty = eventqueue.empty();  // check if empty
    QueueUnlock();                    // unlock
    
    pthread_mutex_lock(&eventmutex);  // lock event mutex (for condition)
    if (empty)
    {
        pthread_cond_wait(&eventclock, &eventmutex); // wait forever if empty
    }
    
    考虑如果队列最初为空,但另一个线程与此冲突,并在
    QueueUnlock()
    pthread\u mutex\u lock(&eventmutex)
    之间推送一个新值,会发生什么情况-新事件的唤醒将丢失。还请注意,在
    SleepUntilNextEvent()
    中,您可以访问
    eventqueue.top()
    void Scheduler::RunEventLoop()
    {
    
        pthread_mutex_lock(&queuemutex);
        while (threadrunning)
        {
            while (!eventqueue.empty() && e.Due())
            {                          // while pending due events exist
                Event e = eventqueue.top();
                eventqueue.pop();
    
                pthread_mutex_unlock(&queuemutex);
                e.DoEvent();           // perform the event
                e.Next();              // decrement repeat counter
                pthread_mutex_lock(&queuemutex);
                                       // reschedule event if necessary
                if (e.ShouldReschedule()) eventqueue.push(e);
            }
    
            SleepUntilNextEvent();     // wait for something to happen
        }
        pthread_mutex_unlock(&queuemutex);
    
        return;                        // if threadrunning is set to false, exit
    }
    
    /* Note: Called with queuemutex held */
    void Scheduler::SleepUntilNextEvent()
    {
        if (eventqueue.empty())
        {
            pthread_cond_wait(&eventclock, &queuemutex); // wait forever if empty
        }
        else
        {
            timespec t =                  // get absolute time of wakeup
                Bottime::GetMillisAsTimespec(eventqueue.top().Countdown() + 
                                             Bottime::GetCurrentTimeMillis());
            pthread_cond_timedwait(&eventclock, &queuemutex, &t); // sleep until event
        }
    }