bạn muốn sử dụng tín hiệu hoặc chủ đề?
Trước tiên, hãy thiết lập bộ xử lý tín hiệu hoặc chuẩn bị chức năng chỉ phù hợp; xem man 7 sigevent để biết chi tiết.
Tiếp theo, tạo bộ hẹn giờ phù hợp, sử dụng timer_create()
. Xem man 2 timer_create để biết chi tiết.
Tùy thuộc vào những gì bạn làm khi bộ hẹn giờ kích hoạt, bạn có thể muốn đặt hẹn giờ thành một lần hoặc lặp lại sau một khoảng thời gian ngắn. Bạn sử dụng timer_settime()
cho cả hai cánh tay và để giải giáp vũ khí, bộ hẹn giờ; xem man 2 timer_settime để biết chi tiết.
Trong các ứng dụng thực tế, bạn thường cần phải ghép giờ. Mặc dù một quá trình có thể tạo nhiều bộ tính giờ, nhưng chúng là một tài nguyên giới hạn. Đặc biệt là thời gian chờ - không quan trọng, hoặc đặt cờ và/hoặc gửi tín hiệu đến một chuỗi cụ thể - nên sử dụng một bộ hẹn giờ duy nhất, sẽ kích hoạt vào thời gian chờ tiếp theo, đặt cờ thời gian chờ có liên quan và tùy chọn gửi tín hiệu (với một bộ xử lý rỗng) đến chuỗi mong muốn để đảm bảo nó bị gián đoạn. (Đối với một quá trình đơn luồng, việc gửi tín hiệu ban đầu sẽ làm gián đoạn các cuộc gọi I/O chặn.) Xem xét một máy chủ, trả lời một số yêu cầu: yêu cầu có thể có thời gian chờ trên một phút hoặc lâu hơn, trong khi xử lý yêu cầu có thể cần thời gian chờ kết nối, thời gian chờ I/O, v.v.
Bây giờ, câu hỏi ban đầu là thú vị, bởi vì bộ hẹn giờ mạnh mẽ khi được sử dụng hiệu quả. Tuy nhiên, chương trình ví dụ về cơ bản là vô nghĩa. Tại sao bạn không tạo ra một chương trình đặt một hoặc nhiều bộ tính giờ, mỗi ví dụ đưa ra một cái gì đó cho đầu ra tiêu chuẩn? Hãy nhớ sử dụng write()
et al từ unistd.h
vì chúng là async-signal safe, trong khi printf()
et cetera từ stdio.h
thì không. (Nếu trình xử lý tín hiệu của bạn sử dụng các chức năng an toàn không đồng bộ, kết quả là không xác định. Nó thường hoạt động, nhưng nó không được đảm bảo chút nào; nó cũng có thể bị hỏng như công việc. .)
Chỉnh sửa để thêm: Đây là ví dụ hoàn hảo về thời gian chờ ghép kênh.
(Trong phạm vi có thể theo luật pháp, tôi dành tất cả quyền bản quyền và quyền liên quan và lân cận cho các đoạn mã được hiển thị bên dưới vào phạm vi công cộng trên toàn thế giới; xem CC0 Public Domain Dedication. Nói cách khác, vui lòng sử dụng mã bên dưới theo bất kỳ cách nào bạn muốn, đừng đổ lỗi cho tôi vì bất kỳ vấn đề nào với nó.)
Tôi đã sử dụng các trình xây dựng nguyên tử GCC kiểu cũ, vì vậy nó phải an toàn chỉ. Với một vài bổ sung, nó cũng làm việc cho mã đa luồng. (Bạn không thể sử dụng ví dụ mutexes, vì pthread_mutex_lock()
là không an toàn async-tín hiệu. Atomically thao tác các tiểu bang timeout nên làm việc, mặc dù có thể có một số cuộc đua trái nếu bạn vô hiệu hóa một thời gian chờ chỉ khi nó cháy.)
#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;
}
Hãy nhớ bao gồm thư viện rt
khi biên dịch, tức là sử dụng gcc -W -Wall *source*.c -lrt -o *binary*
để biên dịch.
Ý tưởng là chương trình chính đầu tiên gọi timeout_init()
để cài đặt tất cả các trình xử lý cần thiết và cetera, và có thể gọi timeout_done()
để deistall nó trước khi thoát (hoặc trong một quá trình con sau fork()
ing).
Để đặt thời gian chờ, bạn gọi timeout_set(seconds)
. Giá trị trả về là một bộ mô tả thời gian chờ. Hiện tại chỉ có một lá cờ bạn có thể kiểm tra bằng cách sử dụng timeout_passed()
, nhưng việc phân phối tín hiệu thời gian chờ cũng làm gián đoạn bất kỳ cuộc gọi I/O chặn nào. Vì vậy, bạn có thể mong đợi thời gian chờ để ngắt bất kỳ cuộc gọi I/O chặn nào.
Nếu bạn muốn làm bất cứ điều gì hơn là đặt cờ vào thời gian chờ, bạn không thể thực hiện điều đó trong trình xử lý tín hiệu; hãy nhớ, trong một trình xử lý tín hiệu, bạn bị giới hạn các chức năng an toàn không đồng bộ-tín hiệu. Cách dễ nhất xung quanh đó là sử dụng một sợi riêng biệt với vòng lặp vô tận trên sigwaitinfo()
, với tín hiệu TIMEOUT_SIGNAL
bị chặn trong tất cả các chuỗi khác. Bằng cách đó, các sợi chuyên dụng được đảm bảo để bắt tín hiệu, nhưng đồng thời, không giới hạn chức năng an toàn không đồng bộ tín hiệu. Nó có thể, ví dụ, làm nhiều việc hơn, hoặc thậm chí gửi một tín hiệu đến một sợi cụ thể bằng cách sử dụng pthread_kill()
. (Miễn là tín hiệu đó có một bộ xử lý, ngay cả một với một cơ thể trống, giao hàng của nó sẽ làm gián đoạn bất kỳ cuộc gọi I/O chặn trong chủ đề đó.)
Đây là một ví dụ đơn giản main()
để sử dụng hết thời gian chờ. Nó là ngớ ngẩn, và dựa trên fgets()
không thử lại (khi bị gián đoạn bởi một tín hiệu), nhưng có vẻ như nó hoạt động.
#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;
}
Điều này 'i
Bạn có thể muốn kiểm tra [this] (http://stackoverflow.com/questions/5540245/loops-timers-in-c) câu hỏi – tomahh
sai lầm của tôi, tôi có nghĩa là sizeof (a)/sizeof (int), tôi sẽ sửa đổi mã. –