8

重複の可能性:
C のループ/タイマー

私は過去 3 日間、タイマーについて読んでいましたが、役立つものを見つけることができませんでした。実際の例で理解しようとしています。誰かが以下のプログラムのアラームを設定する方法を理解するのを手伝ってくれますか?

2つの引数を送信するようにタイマーを設定するにはどうすればよいですか.1つは配列名で、2つ目は削除する番号です.関数を呼び出すための引数を持つアラーム。

環境は Linux であることに注意してください。また、動作する C の例とのリンクを歓迎します。

#include<stdio.h>
int delete_from_array(int arg) ;


    int main()
    {

    int a[10000], i, y ;
    //how to set timer here for to delete any number in array after half a second
    for (y=0; y < 100; y++) {


        for (i=0; i<sizeof(a) / sizeof(int); i++)
            a[i] = i;
    sleep(1);
    printf("wake\n");
    }

    }

    int delete_from_array(int arg) 
    {
    int i, a[1000], number_to_delete=0;

    //number_to_delete = arg->number;

    for (i=0; i<sizeof(a); i++)
        if (a[i] == number_to_delete)
            a[i] = 0;
    printf("deleted\n");

    }

私がやろうとしているのは、1秒後に期限切れになる値を持つハッシュがあるため、値をハッシュに挿入した後、タイマーを作成して、その値を削除する必要があるということです。 1秒、そしてその間隔(1秒)の前にサーバーから応答を受け取った場合、ハッシュから値を削除し、タイマーを削除します.tcpでの再送信とほぼ同じです

4

2 に答える 2

25

シグナルまたはスレッドを使用しますか?

まず、シグナル ハンドラを設定するか、適切なスレッド関数を準備します。詳細については、 man 7 sigeventを参照してください。

次に、 を使用して適切なタイマーを作成しtimer_create()ます。詳細については、 man 2 timer_createを参照してください。

タイマーが起動したときに何をするかによって、タイマーをワンショットに設定するか、その後短い間隔で繰り返すように設定できます。timer_settime()タイマーのアームとアーム解除の両方に使用します。詳細については、 man 2 timer_settimeを参照してください。

実際のアプリケーションでは、通常、タイマーを多重化する必要があります。プロセスは複数のタイマーを作成できますが、それらは限られたリソースです。特に、フラグを設定したり、特定のスレッドにシグナルを送信したりするのは簡単なタイムアウトタイマーは、次のタイムアウトで起動し、関連するタイムアウトフラグを設定し、オプションでシグナルを送信する単一のタイマーを使用する必要があります (を目的のスレッドに渡して、確実に中断されるようにします。(シングル スレッド プロセスの場合、元のシグナル配信は I/O 呼び出しのブロックを中断します。) 何らかの要求に応答するサーバーを考えてみましょう。要求の処理中に、要求自体に 1 分程度のタイムアウトが発生する場合があります。接続タイムアウト、I/O タイムアウトなどが必要になる場合があります。

タイマーは効果的に使用すると強力になるため、元の質問は興味深いものです。ただし、サンプルプログラムは基本的にナンセンスです。たとえば、標準出力に何かを出力するなど、1 つまたは複数のタイマーを設定するプログラムを作成してみませんか? write()et al fromunistd.hasync-signal safeであるため、忘れずに使用してください。一方、printf()et cetera fromstdio.hはそうではありません。(シグナルハンドラーが非非同期シグナルセーフ関数を使用している場合、結果は未定義です。通常は機能しますが、まったく保証されていません。機能するのと同じくらいクラッシュする可能性があります。 undefinedであるため、テストではわかりません。)


編集して追加: これは、多重化されたタイムアウトの最低限の例です。

(法律の下で可能な限り、私は以下に示すコード スニペットに対するすべての著作権、関連する権利、および隣接する権利を世界中のパブリック ドメインに捧げます。CC0 パブリック ドメインへの献身を参照してください。つまり、以下のコードをどのような方法でも自由に使用してください。それに関する問題について私を責めないでください.)

古いスタイルの GCC アトミック ビルトインを使用したため、スレッドセーフである必要があります。いくつか追加すれば、マルチスレッド コードでも機能するはずです。(async-signal safe ではないため、たとえばミューテックスを使用することはできませんpthread_mutex_lock()。タイムアウト状態を原子的に操作することは機能するはずですが、タイムアウトが発生したときにタイムアウトを無効にすると、いくつかの競合が残る可能性があります。)

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

#define   TIMEOUTS       16
#define   TIMEOUT_SIGNAL (SIGRTMIN+0)

#define   TIMEOUT_USED   1
#define   TIMEOUT_ARMED  2
#define   TIMEOUT_PASSED 4

static timer_t               timeout_timer;
static volatile sig_atomic_t timeout_state[TIMEOUTS] = { 0 };
static struct timespec       timeout_time[TIMEOUTS];


/* Return the number of seconds between before and after, (after - before).
 * This must be async-signal safe, so it cannot use difftime().
*/
static inline double timespec_diff(const struct timespec after, const struct timespec before)
{
    return (double)(after.tv_sec - before.tv_sec)
         + (double)(after.tv_nsec - before.tv_nsec) / 1000000000.0;
}

/* Add positive seconds to a timespec, nothing if seconds is negative.
 * This must be async-signal safe.
*/
static inline void timespec_add(struct timespec *const to, const double seconds)
{
    if (to && seconds > 0.0) {
        long  s = (long)seconds;
        long  ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));

        /* Adjust for rounding errors. */
        if (ns < 0L)
            ns = 0L;
        else
        if (ns > 999999999L)
            ns = 999999999L;

        to->tv_sec += (time_t)s;
        to->tv_nsec += ns;

        if (to->tv_nsec >= 1000000000L) {
            to->tv_nsec -= 1000000000L;
            to->tv_sec++;
        }
    }
}

/* Set the timespec to the specified number of seconds, or zero if negative seconds.
*/
static inline void timespec_set(struct timespec *const to, const double seconds)
{
    if (to) {
        if (seconds > 0.0) {
            const long  s = (long)seconds;
            long       ns = (long)(0.5 + 1000000000.0 * (seconds - (double)s));

            if (ns < 0L)
                ns = 0L;
            else
            if (ns > 999999999L)
                ns = 999999999L;

            to->tv_sec = (time_t)s;
            to->tv_nsec = ns;

        } else {
            to->tv_sec = (time_t)0;
            to->tv_nsec = 0L;
        }
    }
}


/* Return nonzero if the timeout has occurred.
*/
static inline int timeout_passed(const int timeout)
{
    if (timeout >= 0 && timeout < TIMEOUTS) {
        const int  state = __sync_or_and_fetch(&timeout_state[timeout], 0);

        /* Refers to an unused timeout? */
        if (!(state & TIMEOUT_USED))
            return -1;

        /* Not armed? */
        if (!(state & TIMEOUT_ARMED))
            return -1;

        /* Return 1 if timeout passed, 0 otherwise. */
        return (state & TIMEOUT_PASSED) ? 1 : 0;

    } else {
        /* Invalid timeout number. */
        return -1;
    }
}

/* Release the timeout.
 * Returns 0 if the timeout had not fired yet, 1 if it had.
*/
static inline int timeout_unset(const int timeout)
{
    if (timeout >= 0 && timeout < TIMEOUTS) {
        /* Obtain the current timeout state to 'state',
         * then clear all but the TIMEOUT_PASSED flag
         * for the specified timeout.
         * Thanks to Bylos for catching this bug. */
        const int  state = __sync_fetch_and_and(&timeout_state[timeout], TIMEOUT_PASSED);

        /* Invalid timeout? */
        if (!(state & TIMEOUT_USED))
            return -1;

        /* Not armed? */
        if (!(state & TIMEOUT_ARMED))
            return -1;

        /* Return 1 if passed, 0 otherwise. */
        return (state & TIMEOUT_PASSED) ? 1 : 0;

    } else {
        /* Invalid timeout number. */
        return -1;
    }
}


int timeout_set(const double seconds)
{
    struct timespec   now, then;
    struct itimerspec when;
    double            next;
    int               timeout, i;

    /* Timeout must be in the future. */
    if (seconds <= 0.0)
        return -1;

    /* Get current time, */
    if (clock_gettime(CLOCK_REALTIME, &now))
        return -1;

    /* and calculate when the timeout should fire. */
    then = now;
    timespec_add(&then, seconds);

    /* Find an unused timeout. */
    for (timeout = 0; timeout < TIMEOUTS; timeout++)
        if (!(__sync_fetch_and_or(&timeout_state[timeout], TIMEOUT_USED) & TIMEOUT_USED))
            break;

    /* No unused timeouts? */
    if (timeout >= TIMEOUTS)
        return -1;

    /* Clear all but TIMEOUT_USED from the state, */
    __sync_and_and_fetch(&timeout_state[timeout], TIMEOUT_USED);

    /* update the timeout details, */
    timeout_time[timeout] = then;

    /* and mark the timeout armable. */
    __sync_or_and_fetch(&timeout_state[timeout], TIMEOUT_ARMED);

    /* How long till the next timeout? */
    next = seconds;
    for (i = 0; i < TIMEOUTS; i++)
        if ((__sync_fetch_and_or(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) {
            const double secs = timespec_diff(timeout_time[i], now);
            if (secs >= 0.0 && secs < next)
                next = secs;
        }

    /* Calculate duration when to fire the timeout next, */
    timespec_set(&when.it_value, next);
    when.it_interval.tv_sec = 0;
    when.it_interval.tv_nsec = 0L;

    /* and arm the timer. */
    if (timer_settime(timeout_timer, 0, &when, NULL)) {
        /* Failed. */
        __sync_and_and_fetch(&timeout_state[timeout], 0);
        return -1;
    }

    /* Return the timeout number. */
    return timeout;
}


static void timeout_signal_handler(int signum __attribute__((unused)), siginfo_t *info, void *context __attribute__((unused)))
{
    struct timespec   now;
    struct itimerspec when;
    int               saved_errno, i;
    double            next;

    /* Not a timer signal? */
    if (!info || info->si_code != SI_TIMER)
        return;

    /* Save errno; some of the functions used may modify errno. */
    saved_errno = errno;

    if (clock_gettime(CLOCK_REALTIME, &now)) {
        errno = saved_errno;
        return;
    }

    /* Assume no next timeout. */
    next = -1.0;

    /* Check all timeouts that are used and armed, but not passed yet. */
    for (i = 0; i < TIMEOUTS; i++)
        if ((__sync_or_and_fetch(&timeout_state[i], 0) & (TIMEOUT_USED | TIMEOUT_ARMED | TIMEOUT_PASSED)) == (TIMEOUT_USED | TIMEOUT_ARMED)) {
            const double  seconds = timespec_diff(timeout_time[i], now);
            if (seconds <= 0.0) {
                /* timeout [i] fires! */
                __sync_or_and_fetch(&timeout_state[i], TIMEOUT_PASSED);

            } else
            if (next <= 0.0 || seconds < next) {
                /* This is the soonest timeout in the future. */
                next = seconds;
            }
        }

    /* Note: timespec_set() will set the time to zero if next <= 0.0,
     *       which in turn will disarm the timer.
     * The timer is one-shot; it_interval == 0.
    */
    timespec_set(&when.it_value, next);
    when.it_interval.tv_sec = 0;
    when.it_interval.tv_nsec = 0L;
    timer_settime(timeout_timer, 0, &when, NULL);

    /* Restore errno. */
    errno = saved_errno;
}


int timeout_init(void)
{
    struct sigaction  act;
    struct sigevent   evt;
    struct itimerspec arm;

    /* Install timeout_signal_handler. */
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = timeout_signal_handler;
    act.sa_flags = SA_SIGINFO;
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL))
        return errno;

    /* Create a timer that will signal to timeout_signal_handler. */
    evt.sigev_notify = SIGEV_SIGNAL;
    evt.sigev_signo = TIMEOUT_SIGNAL;
    evt.sigev_value.sival_ptr = NULL;
    if (timer_create(CLOCK_REALTIME, &evt, &timeout_timer))
        return errno;

    /* Disarm the timeout timer (for now). */
    arm.it_value.tv_sec = 0;
    arm.it_value.tv_nsec = 0L;
    arm.it_interval.tv_sec = 0;
    arm.it_interval.tv_nsec = 0L;
    if (timer_settime(timeout_timer, 0, &arm, NULL))
        return errno;

    return 0;
}

int timeout_done(void)
{
    struct sigaction  act;
    struct itimerspec arm;
    int               errors = 0;

    /* Ignore the timeout signals. */
    sigemptyset(&act.sa_mask);
    act.sa_handler = SIG_IGN;
    if (sigaction(TIMEOUT_SIGNAL, &act, NULL))
        if (!errors) errors = errno;

    /* Disarm any current timeouts. */
    arm.it_value.tv_sec = 0;
    arm.it_value.tv_nsec = 0L;
    arm.it_interval.tv_sec = 0;
    arm.it_interval.tv_nsec = 0;
    if (timer_settime(timeout_timer, 0, &arm, NULL))
        if (!errors) errors = errno;

    /* Destroy the timer itself. */
    if (timer_delete(timeout_timer))
        if (!errors) errors = errno;

    /* If any errors occurred, set errno. */
    if (errors)
        errno = errors;

    /* Return 0 if success, errno otherwise. */
    return errors;
}

rtコンパイル時にライブラリを含めることを忘れないでください。つまり、コンパイルに使用gcc -W -Wall *source*.c -lrt -o *binary*します。

アイデアは、メインプログラムが最初timeout_init()にすべての必要なハンドラーなどをインストールするために呼び出し、終了する前に (またはingtimeout_done()後の子プロセスで) 呼び出してそれをアンインストールすることができるというものです。fork()

タイムアウトを設定するには、 を呼び出しますtimeout_set(seconds)。戻り値はタイムアウト記述子です。現在、 を使用して確認できるフラグのみがありますがtimeout_passed()、タイムアウト シグナルの配信により、ブロッキング I/O 呼び出しも中断されます。したがって、タイムアウトによってブロッキング I/O 呼び出しが中断されることが予想されます。

タイムアウト時にフラグを設定する以外のことをしたい場合は、シグナル ハンドラーでは実行できません。シグナルハンドラーでは、非同期シグナルセーフ関数に限定されていることを覚えておいてください。これを回避する最も簡単な方法は、他のすべてのスレッドでシグナルがブロックされた状態でsigwaitinfo()、 を無限ループする別のスレッドを使用することです。TIMEOUT_SIGNALそうすれば、専用スレッドがシグナルをキャッチすることが保証されますが、同時に、非同期シグナルセーフ関数に限定されません。たとえば、より多くの作業を実行したり、 を使用して特定のスレッドにシグナルを送信したりすることもできますpthread_kill()。(そのシグナルにハンドラーがある限り、ボディが空のハンドラーであっても、その配信はそのスレッド内のブロッキング I/O 呼び出しを中断します。)

main()タイムアウトを使用する簡単な例を次に示します。fgets()それはばかげており、 (シグナルによって中断された場合) 再試行しないことに依存していますが、うまくいくようです。

#include <string.h>
#include <stdio.h>

int main(void)
{
    char    buffer[1024], *line;
    int t1, t2, warned1;

    if (timeout_init()) {
        fprintf(stderr, "timeout_init(): %s.\n", strerror(errno));
        return 1;
    }

    printf("You have five seconds to type something.\n");
    t1 = timeout_set(2.5); warned1 = 0;
    t2 = timeout_set(5.0);
    line = NULL;

    while (1) {

        if (timeout_passed(t1)) {
            /* Print only the first time we notice. */
            if (!warned1++)
                printf("\nTwo and a half seconds left, buddy.\n");
        }

        if (timeout_passed(t2)) {
            printf("\nAw, just forget it, then.\n");
            break;
        }

        line = fgets(buffer, sizeof buffer, stdin);
        if (line) {
            printf("\nOk, you typed: %s\n", line);
            break;
        }
    }

    /* The two timeouts are no longer needed. */
    timeout_unset(t1);
    timeout_unset(t2);

    /* Note: 'line' is non-NULL if the user did type a line. */

    if (timeout_done()) {
        fprintf(stderr, "timeout_done(): %s.\n", strerror(errno));
        return 1;
    }

    return 0;
}
于 2012-10-07T00:39:28.457 に答える
3

time(7)の man ページを読むと便利です。Linux はtimerfd_create(2) Linux 固有のsyscallも提供することに注意してください。これは、しばしばpoll(2) (またはppoll(2)古いselect(2)syscall) のような多重化 syscall と共に使用されます。

シグナルを使用する場合は、 signal(7)の man ページを注意深く読むことを忘れないでください (シグナル ハンドラーのコーディングには制限があります。volatile sigatomic_tシグナル ハンドラーに変数を設定する必要があるかもしれません。newまたは-deleteまたはmalloc& free-を実行しないでください)。非同期セーフな関数呼び出しのみが許可されるシグナル ハンドラー内のメモリ管理操作)。

また、GUI アプリケーションなどのイベント指向プログラミングは、多くの場合libevent、イベント ループでタイマーを管理する方法 (Gtk、Qt、with ...) を提供することに注意してください。

于 2012-10-07T06:42:19.930 に答える