這不是一個答案,而是一個如何使用信號和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
,則可以使用例如編譯它
gcc -W -Wall -O3 -std=c99 -pedantic timer.c -lrt -o timer
並使用./timer
運行它。
如果仔細閱讀上面的代碼,您會發現它實際上是一個週期性定時器信號(以毫秒爲間隔),在第一個信號之前有一個可變延遲。這只是我喜歡用來確保我不會錯過信號的技術。 (信號重複,直到超時未設置。)
請注意,儘管您可以在信號處理程序中進行計算,但您應該只使用異步信號安全;見man 7 signal。另外,只有sig_atomic_t
類型是原子型的。普通的單線程代碼和一個信號處理程序。所以,最好只用信號作爲指標,並在自己的程序中做實際的代碼。
如果您想要更新信號處理程序中的怪物座標,這是可能的,但有點棘手。我會用三個包含怪物信息的數組,並使用GCC __sync_bool_compare_and_swap()
來更新數組指針 - 與圖形中的三重緩衝非常相似。
如果您需要多個併發超時,則可以使用多個定時器(有多個定時器可用),但最好的選擇是定義超時槽。 (您可以使用生成計數器來檢測「已忘記」的超時等等。)每當設置或取消設置新超時時,都會更新超時以反映下一個超時過期。這是一個更多的代碼,但是真的是上述的直接擴展。
是的,我已經使用-lrt – 2012-12-16 19:21:10
鏈接它歡迎來到程序員。請花點時間閱讀該網站的[常見問題],您將在這裏找到關於提問的一些好信息。在處理代碼實現時,這個問題可能更適合於SO。請不要在那裏重新提問,因爲這可以遷移。遵循的一般規則是如果你的問題在你的IDE之前,它就屬於SO。如果它在白板前面,則屬於程序員。 – Walter