0

Linux で clock_gettime を使用してカウントダウン タイマーを作成する方法を説明してください。clock() 関数を使用して CPU 時間を取得し、それを CLOCKS_PER_SEC で乗算して実際の時間を取得できることは知っていますが、clock() 関数はこれにはあまり適していないと言われています。

これまでのところ、私はこれを試みました(10億は1秒間停止することです)

#include <stdio.h>
#include <time.h>

#define BILLION 1000000000

int main()
{
 struct timespec rawtime;
 clock_gettime(CLOCK_MONOTONIC_RAW, &rawtime);
 unsigned long int current =  ( rawtime.tv_sec + rawtime.tv_nsec );
 unsigned long int end =  (( rawtime.tv_sec + rawtime.tv_nsec ) + BILLION );
 while ( current < end )
 {
  clock_gettime(CLOCK_MONOTONIC_RAW, &rawtime);
  current =  ( rawtime.tv_sec + rawtime.tv_nsec );
 }

 return 0;
}

これだけではあまり役に立たないことはわかっていますが、正しく時間を計る方法がわかれば、これを自分のプロジェクトで使用できます。この目的で sleep() を使用できることはわかっていますが、タイマーを自分でコーディングして、プログラム全体を一時停止するのではなく、残り時間を返す可能性など、自分のプロジェクトにうまく統合できるようにしたいと考えています。 .

4

2 に答える 2

5

そうしないでください。ビジー ループで CPU パワーを無駄に消費しています。

nanosleep()代わりに関数を使用しないのはなぜですか? 概説したユースケースに完全に適しています。または、より簡単なインターフェースが必要な場合は、おそらく次のようなものです

#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <errno.h>

/* Sleep for the specified number of seconds,
 * and return the time left over.
*/
double dsleep(const double seconds)
{
    struct timespec  req, rem;

    /* No sleep? */
    if (seconds <= 0.0)
        return 0.0;

    /* Convert to seconds and nanoseconds. */
    req.tv_sec = (time_t)seconds;
    req.tv_nsec = (long)((seconds - (double)req.tv_sec) * 1000000000.0);

    /* Take care of any rounding errors. */
    if (req.tv_nsec < 0L)
        req.tv_nsec = 0L;
    else
    if (req.tv_nsec > 999999999L)
        req.tv_nsec = 999999999L;

    /* Do the nanosleep. */
    if (nanosleep(&req, &rem) != -1)
        return 0.0;

    /* Error? */
    if (errno != EINTR)
        return 0.0;

    /* Return remainder. */
    return (double)rem.tv_sec + (double)rem.tv_nsec / 1000000000.0;
}

違いは、これを使用すると、CPU が速度に狂ったリスのように回転するのではなく、他のことを自由に実行できることです。

于 2012-12-16T20:53:21.323 に答える
2

これは答えではありませんが、シグナルとPOSIXタイマーを使用してタイムアウトタイマーを実装する方法の例です。受け入れられた回答へのコメントでのOPのフォローアップ質問への回答として意図されています。

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

/* Timeout timer.
*/
static timer_t                  timeout_timer;
static volatile sig_atomic_t    timeout_state = 0;
static volatile sig_atomic_t    timeout_armed = 2;
static const int                timeout_signo = SIGALRM;

#define TIMEDOUT() (timeout_state != 0)

/* Timeout signal handler.
*/
static void timeout_handler(int signo, siginfo_t *info, void *context __attribute__((unused)))
{
    if (timeout_armed == 1)
        if (signo == timeout_signo && info && info->si_code == SI_TIMER)
            timeout_state = ~0;
}

/* Unset timeout.
 * Returns nonzero if timeout had expired, zero otherwise.
*/
static int timeout_unset(void)
{
    struct itimerspec   t;   
    const int           retval = timeout_state;

    /* Not armed? */
    if (timeout_armed != 1)
        return retval;

    /* Disarm. */
    t.it_value.tv_sec = 0;
    t.it_value.tv_nsec = 0;
    t.it_interval.tv_sec = 0;
    t.it_interval.tv_nsec = 0;
    timer_settime(timeout_timer, 0, &t, NULL);

    return retval;
}

/* Set timeout (in wall clock seconds).
 * Cancels any pending timeouts.
*/
static int timeout_set(const double seconds)
{
    struct itimerspec  t;

    /* Uninitialized yet? */
    if (timeout_armed == 2) {
        struct sigaction    act;
        struct sigevent     evt;

        /* Use timeout_handler() for timeout_signo signal. */
        sigemptyset(&act.sa_mask);
        act.sa_sigaction = timeout_handler;
        act.sa_flags = SA_SIGINFO;

        if (sigaction(timeout_signo, &act, NULL) == -1)
            return errno;

        /* Create a monotonic timer, delivering timeout_signo signal. */
        evt.sigev_value.sival_ptr = NULL;
        evt.sigev_signo = timeout_signo;
        evt.sigev_notify = SIGEV_SIGNAL;

        if (timer_create(CLOCK_MONOTONIC, &evt, &timeout_timer) == -1)
            return errno;

        /* Timeout is initialzied but unarmed. */
        timeout_armed = 0;
    }

    /* Disarm timer, if armed. */
    if (timeout_armed == 1) {

        /* Set zero timeout, disarming the timer. */
        t.it_value.tv_sec = 0;
        t.it_value.tv_nsec = 0;
        t.it_interval.tv_sec = 0;
        t.it_interval.tv_nsec = 0;

        if (timer_settime(timeout_timer, 0, &t, NULL) == -1)
            return errno;

        timeout_armed = 0;
    }

    /* Clear timeout state. It should be safe (no pending signals). */
    timeout_state = 0;

    /* Invalid timeout? */
    if (seconds <= 0.0)
        return errno = EINVAL;

    /* Set new timeout. Check for underflow/overflow. */
    t.it_value.tv_sec = (time_t)seconds;
    t.it_value.tv_nsec = (long)((seconds - (double)t.it_value.tv_sec) * 1000000000.0);
    if (t.it_value.tv_nsec < 0L)
        t.it_value.tv_nsec = 0L;
    else
    if (t.it_value.tv_nsec > 999999999L)
        t.it_value.tv_nsec = 999999999L;

    /* Set it repeat once every millisecond, just in case the initial
     * interrupt is missed. */
    t.it_interval.tv_sec = 0;
    t.it_interval.tv_nsec = 1000000L;

    if (timer_settime(timeout_timer, 0, &t, NULL) == -1)
        return errno;

    timeout_armed = 1;

    return 0;
}


int main(void)
{
    char    *line = NULL;
    size_t   size = 0;
    ssize_t  len;

    fprintf(stderr, "Please supply input. The program will exit automatically if\n");
    fprintf(stderr, "it takes more than five seconds for the next line to arrive.\n");
    fflush(stderr);

    while (1) {

        if (timeout_set(5.0)) {
            const char *const errmsg = strerror(errno);
            fprintf(stderr, "Cannot set timeout: %s.\n", errmsg);
            return 1;
        }

        len = getline(&line, &size, stdin);
        if (len == (ssize_t)-1)
            break;

        if (len < (ssize_t)1) {
            /* This should never occur (except for -1, of course). */
            errno = EIO;
            break;
        }

        /* We do not want *output* to be interrupted,
         * so we cancel the timeout. */
        timeout_unset();

        if (fwrite(line, (size_t)len, 1, stdout) != 1) {
            fprintf(stderr, "Error writing to standard output.\n");
            fflush(stderr);
            return 1;
        }
        fflush(stdout);

        /* Next line. */
    }

    /* Remember to cancel the timeout. Also check it. */
    if (timeout_unset())
        fprintf(stderr, "Timed out.\n");
    else
    if (ferror(stdin) || !feof(stdin))
        fprintf(stderr, "Error reading standard input.\n");
    else
        fprintf(stderr, "End of input.\n");

    fflush(stderr);

    /* Free line buffer. */
    free(line);
    line = NULL;
    size = 0;

    /* Done. */
    return 0;
}

上記をとして保存するtimer.cと、egを使用してコンパイルできます。

gcc -W -Wall -O3 -std=c99 -pedantic timer.c -lrt -o timer

を使用して実行し./timerます。

上記のコードを注意深く読むと、実際には(ミリ秒間隔の)定期的なタイマー信号であり、最初の信号の前に可変の遅延があることがわかります。これは、信号を見逃さないようにするために使用するのが好きなテクニックです。(タイムアウトが設定されなくなるまで信号が繰り返されます。)

シグナルハンドラーで計算を行うことはできますが、非同期シグナルセーフな関数のみを使用する必要があることに注意してください。男7信号を参照してください。また、sig_atomic_tタイプのみがアトミックwrtです。通常のシングルスレッドコードとシグナルハンドラ。したがって、シグナルをインジケーターとして使用し、実際のコードを独自のプログラムで実行することをお勧めします。

たとえば、シグナルハンドラでモンスターの座標を更新したい場合、それは可能ですが、少し注意が必要です。モンスター情報を含む3つの配列を使用し、GCCを使用__sync_bool_compare_and_swap()して配列ポインターを更新します。これは、グラフィックスのトリプルバッファリングとほぼ同じ手法です。

複数の同時タイムアウトが必要な場合は、複数のタイマーを使用できます(使用可能なタイマーは多数あります)が、最適なオプションはタイムアウトスロットを定義することです。(生成カウンターを使用して、「忘れられた」タイムアウトなどを検出できます。)新しいタイムアウトが設定または設定解除されるたびに、タイムアウトを更新して、次に期限切れになるタイムアウトを反映します。これはもう少しコードですが、実際には上記の単純な拡張です。

于 2012-12-17T22:19:15.633 に答える