13

我正在阅读我的 STL 实现(标准问题g++ 4.6.2),并在以下内容中遇到了这种竞争条件condition_variable

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)似乎是正确的:

26

效果:好像

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

事实上,底层的 pthreads 实现似乎是问题所在:

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正确实现类似的东西?是否有现有的实现可以做到这一点?还是我错过了什么?

4

1 回答 1

9

诀窍是使用 apthread_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;
}

我将把这个开放一段时间,因为有人可能有一个更简单的答案。我在这里不满意的是,这意味着 await_until很难用实时时钟来实现(我最好的解决方案是将 in 中提供Clock的时间time_point转换为steady_clock' 时间并从那里开始......它仍然会受到时间变化的竞争条件的影响,但是如果您实时指定超时,那么您已经犯了一个可怕的错误)。

于 2012-08-10T05:21:10.007 回答