我怎样才能唤醒睡眠的pthread?

我正在用C ++编写一个程序。 我注意到,它正在获得一些线程,其目的是间隔地做一些事情,其中​​有3或4个。 我决定通过编写一个调度程序服务来重构使用这些线程的其他地方可以订阅的调度程序服务,这可以将我在任何时候运行的额外事件线程的数量减less到一个。

我还没有任何使用这个代码, 在我开始写之前,我想知道是否有可能,并对我的devise得到一些反馈。 我想要完成的简短描述是这样的:

添加事件

  1. 来电者提供一个事件和一个时间表
  2. 时间表提供下一次事件的发生
  3. (事件,时间表)对被添加到事件队列中
  4. 中断睡眠事件线程(即唤醒它)

事件线程主循环

  1. 尝试获取事件队列中的下一个事件
  2. 如果没有未决事件,请直接前往4
  3. 获取下一个事件应该发生的时间
  4. 睡觉,直到下一个事件(或永远,如果没有等待事件)
  5. 如果因任何原因中断了睡眠,请循环回到1
  6. 如果睡眠成功完成,请执行当前事件
  7. 更新队列(删除事件,如果是重复事件则重新插入)
  8. 跳回到1

我已经做了一些研究,并且知道可以中断睡眠线程,并且我相信只要不能同时访问事件队列,就不应该有任何危险的行为。 我可以想象,唤醒一个线程是可能的,在某些情况下,java的Thread的sleep()调用会抛出一个InterruptedExceptionexception,除非它不依赖于操作系统的底层睡眠调用,否则就有可能。

任何人都可以评论我的做法吗? 这是一个轮子,我会更好,而不是重塑? 具体来说,如何中断一个睡眠线程,以便在下一条指令处继续执行,并且是否可以从中断的线程中检测到这种情况?

关于提升的说明

我敢打赌,你可以用boost来编写一个调度程序,但是这个编译和运行在一台机器上,因为缺less一个更好的短语,一堆废话。 我之前编译过boost程序,每个拉动boost的文件通常需要花费30多秒才能编译。 如果我能避免这个令人不愉快的发展障碍,我很想。

附录 – 工作守则[根据咖啡店的build议修订]

这是我制作的代码。 它已经过了基本的testing,但是已经适当地处理了不同延误的单一事件和重复事件。

这是事件线程的主体:

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 } 

这是睡眠function:

 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 } } 

最后,AddEvent:

 void Scheduler::AddEvent(Event e) { QueueLock(); eventqueue.push(e); QueueUnlock(); NotifyEventThread(); } 

相关的variables声明:

 bool threadrunning; priority_queue<Event, vector<Event>, greater<Event> > eventqueue; pthread_mutex_t queuelock; // QueueLock and QueueUnlock operate on this pthread_cond_t eventclock; 

为了处理通用事件的问题,每个Event包含一个指向抽象typesaction对象的指针,子类覆盖action::DoEvent 。 这个方法是从Event::DoEventactions是由他们的事件“拥有”,即如果事件不再需要重新安排,它们将被自动删除。

Solutions Collecting From Web of "我怎样才能唤醒睡眠的pthread?"

你在找什么是pthread_cond_t对象, pthread_cond_timedwaitpthread_cond_wait函数。 你可以创建条件变量isThereAnyTaskToDo并在事件线程中等待它。 当添加新事件时,您只需使用pthread_cond_signal()唤醒事件线程。

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

更新:

POSIX也有一些定时器功能,请参阅create_timer()

我同意格雷格和wilx – pthread_cond_timedwait()可以用来实现你以后的行为。 我只是想补充一点,你可以简化你的事件线程主循环:

  1. 尝试获取事件队列中的下一个事件
  2. 如果没有未决事件,请直接前往4
  3. 获取下一个事件应该发生的时间
  4. 等待条件变量与pthread_cond_timedwait()直到下一个事件(如果没有预定的事件与pthread_cond_wait()
  5. 尝试获取事件队列中的下一个事件
  6. 如果还没有过期的事件,请回到4
  7. 更新队列(删除事件,如果是重复事件则重新插入)
  8. 跳回到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()并在QueueUnlock()pthread_mutex_lock(&eventmutex)之间推入一个新的值 – 新事件的唤醒将被忽略。 另请注意,在SleepUntilNextEvent()您可以在不保留队列锁的情况下访问eventqueue.top()

传递给pthread_cond_wait()的互斥量应该是保护信号相关的共享状态的互斥量。 在这种情况下,“共享状态”就是队列本身,所以你可以使用一个保护队列的互斥体来解决这些问题:

 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 } } 

请注意, pthread_cond_wait()pthread_cond_timedwait()会在等待时释放互斥锁(互斥锁被释放,并且相对于正在发送的互斥锁,等待开始原子级),因此调度器在睡眠时不保持互斥锁。