2012-08-10 105 views
12

我是通过我的STL实现(标准问题g++ 4.6.2)阅读和跨越该位的竞争条件传来condition_variable内:实施condition_variable TIMED_WAIT正确

template<typename _Rep, typename _Period> 
cv_status 
wait_for(unique_lock<mutex>& __lock, 
     const chrono::duration<_Rep, _Period>& __rtime) 
{ 
    return wait_until(__lock, __clock_t::now() + __rtime); 
} 

因为__clock_tstd::chrono::system_clock,我们绑像NTP这样的事情(如果时钟在__clock_t::now() + __rtime后移动了一天,那么我们将等待一天)。

C++标准(30.5.1)似乎得到它的权利:

效果:仿佛

return wait_until(lock, chrono::steady_clock::now() + rel_time);

Boost的condition_variable实现了相同的问题:

template<typename duration_type> 
bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration) 
{ 
    return timed_wait(m,get_system_time()+wait_duration); 
} 

事实上,底层的并行线程执行,似乎是这个问题:

int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
    pthread_mutex_t *restrict mutex, 
    const struct timespec *restrict abstime); 

因为abstime被指定为“系统时间”,而不是单调的时钟。

所以我的问题是:如何正确实施像std::condition_variable::wait_for这样的东西?有没有一个现有的实现来解决这个问题?或者我错过了什么?

+0

注意:在内部,'pthread_cond_timedwait'使用'gettimeofday',如果你想在你指定的时间内超时,它就是假的:http://sourceware.org/git/?p=glibc.git;a=blob; f = nptl/pthread_cond_timedwait.c; h = 7278ec45b0eb0b48be50fe832fcd17ae988dca27; hb = HEAD – 2012-08-10 00:50:40

+0

您可能不得不使用一些其他使用单调时钟的计时器线程,然后在服务员取消之前将服务器唤醒。 – jxh 2012-08-10 00:53:09

+0

如果我必须走得那么远(我非常希望我不这样做),我宁愿在我的'wait_for'函数中做一个旋转等待(看起来像我的'sleep_for'没有这个问题,因为'nanosleep'没有同样的问题,因为它正确使用'CLOCK_MONOTONIC')。 – 2012-08-10 00:58:13

回答

8

诀窍是使用pthread_condattr_setclock来告诉pthread_condattr_t使用CLOCK_MONOTONIC。这样做的C代码很简单:

#include <time.h> 
#include <pthread.h> 

#include <errno.h> 
#include <stdio.h> 

int main() 
{ 
    // Set the clock to be CLOCK_MONOTONIC 
    pthread_condattr_t attr; 
    pthread_condattr_init(&attr); 
    if (int err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) 
    { 
     printf("Error setting clock: %d\n", err); 
    } 

    // Now we can initialize the pthreads objects with that condattr 
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
    pthread_cond_t cond; 
    pthread_cond_init(&cond, &attr); 

    // when getting the time, we must poll from CLOCK_MONOTONIC 
    struct timespec timeout; 
    struct timespec now; 
    clock_gettime(CLOCK_MONOTONIC, &now); 
    timeout.tv_sec = now.tv_sec + 5; 
    timeout.tv_nsec = now.tv_nsec; 

    // business as usual... 
    pthread_mutex_lock(&mutex); 
    int rc = pthread_cond_timedwait(&cond, &mutex, &timeout); 
    if (rc == ETIMEDOUT) 
     printf("Success!\n"); 
    else 
     printf("Got return that wasn't timeout: %d\n", rc); 
    pthread_mutex_unlock(&mutex); 

    return 0; 
} 

我要离开这个开一会儿,因为有人可能有一个更简单的答案。我不高兴在这里的事情是,这意味着一个wait_until是很难实现一个实时时钟(我最好的解决办法是将提供的Clocktime_point转换为steady_clock的时间,并从那里......它仍然受到时间变化的竞赛条件的影响,但如果你实时指定超时时间,那么你已经犯了一个可怕的错误)。

+0

我怀疑是否有更简单的答案,因为它是POSIX API的限制。请注意,不仅'condition_variable'有定时等待的问题,而且'mutex'也有这个问题。此外,对于互斥锁,情况更糟糕,因为没有像pthread_mutexattr_setclock这样的API:http://stackoverflow.com/q/14248033/5447906。 – 2016-05-31 04:22:03