façons d’implémenter un timer dans un thread de travail en C

J’ai un thread de travail qui obtient du travail de pipe. Quelque chose comme ça

void *worker(void *param) { while (!work_done) { read(g_workfds[0], work, sizeof(work)); do_work(work); } } 

Je dois implémenter une timer de 1 seconde dans le même thread pour certaines tâches de comptabilité. Voici ce que je pense:

 void *worker(void *param) { prev_uptime = get_uptime(); while (!work_done) { // set g_workfds[0] as non-block now_uptime = get_uptime(); if (now_uptime - prev_uptime > 1) { do_book_keeping(); prev_uptime = now_uptime; } n = poll(g_workfds[0], 1000); // Wait for 1 second else timeout if (n == 0) // timed out continue; read(g_workfds[0], work, sizeof(work)); do_work(work); // This can take more than 1 second also } } 

J’utilise la disponibilité du système au lieu de l’heure du système car l’heure du système peut être modifiée pendant l’exécution de ce thread. Je me demandais s’il y avait une autre meilleure façon de le faire. Je ne veux pas envisager d’utiliser un autre thread. L’utilisation de alarm() n’est pas une option car elle est déjà utilisée par un autre thread dans le même processus. Ceci est implémenté dans un environnement Linux.

Je suis d’accord avec la plupart de ce que webbi a écrit dans sa réponse. Mais il y a un problème avec sa suggestion d’utiliser le temps plutôt que la disponibilité. Si l’heure du système est mise à jour, elle fonctionnera comme prévu. Mais si l’heure du système est rétablie par exemple de 30 secondes, il n’y aura pas de tenue de livre pendant 30 secondes car (now_time – prev_time) sera négatif (à moins qu’un type non signé soit utilisé, auquel cas il fonctionnera quand même).

Une alternative serait d’utiliser clock_gettime () avec CLOCK_MONOTONIC comme clockid ( http://linux.die.net/man/2/clock_gettime ). Un peu désordonné si vous n’avez pas besoin d’unités de temps plus petites que les secondes.

De plus, append du code pour détecter un saut d’horloge en arrière n’est pas difficile non plus.

J’ai trouvé un meilleur moyen mais c’est spécifique à Linux en utilisant l’appel système timerfd_create () . Il prend en charge le changement d’heure du système. Voici un code pseudo possible:

 void *worker(void *param) { int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // Monotonic doesn't get affected by system time change // set timerfd to non-block timerfd_settime(timerfd, 1 second timer); // timer starts while (!work_done) { // set g_workfds[0] as non-block n = poll(g_workfds[0] and timerfd, 0); // poll on both pipe and timerfd and Wait indefinetly if (timerfd is readable) do_book_keeping(); if (g_workfds[0] is readable) { read(g_workfds[0], work, sizeof(work)); do_work(work); // This can take more than 1 second also } } } 

Il semble plus propre et read() sur timerfd retourne du temps supplémentaire au cas où do_work() prend beaucoup de temps, ce qui est très utile car do_book_keeping() s’attend à être appelé chaque seconde.

J’ai trouvé des choses bizarres dans ton code …

poll () a 3 arguments, vous passez 2, le second est le nombre de structures que vous passez dans le tableau struct du premier paramètre, le troisième paramètre est le délai d’attente.

Référence: http://linux.die.net/man/2/poll

En plus de ça, ça va pour moi cette solution, ce n’est pas le meilleur bien sûr, mais c’est bien sans impliquer un autre thread ou alarme (), etc. Vous utilisez le temps et pas le temps, cela peut vous causer une erreur , mais il continuera à fonctionner car il sera mis à jour et continuera d’attendre 1 seconde, peu importe l’heure.