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_tは であるためstd::chrono::system_clock、NTP などの気まぐれに縛られています (時計が の後__clock_t::now() + __rtimeに 1 日遅れると、1 日待つことになります)。

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

秘訣は、 a を使用して を使用する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、リアルタイムクロックで a を実装するのがかなり難しいことを意味することです (これに対する私の最善の解決策は、 で提供さClockれたtime_pointsteady_clockの時間に変換し、そこから移動することです...それでも時間変更の競合状態の影響を受けますが、リアルタイムでタイムアウトを指定している場合は、すでにひどい間違いを犯しています)。

于 2012-08-10T05:21:10.007 に答える