2012-08-10 175 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