可能性のある複製:
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での再送信とほとんど同じように、ハッシュから値を削除してタイマーを削除します。
シグナルまたはスレッドを使用しますか?
まず、シグナルハンドラを設定するか、適切なスレッド関数を準備します。詳細は man 7 sigevent を参照してください。
次に、timer_create()
を使用して適切なタイマーを作成します。詳細は man 2 timer_create を参照してください。
タイマーが作動したときに何をするかに応じて、タイマーをワンショットに設定するか、または後で短い間隔で繰り返すかを選択できます。タイマーの準備と解除の両方にtimer_settime()
を使用します。詳細は man 2 timer_settime を参照してください。
実際のアプリケーションでは、通常、タイマーを多重化する必要があります。プロセスは複数のタイマーを作成できますが、それらは限られたリソースです。特にタイムアウトタイマー-フラグを設定するか、特定のスレッドに信号を送信するかのいずれかです-は単純ですが、次のタイムアウトで起動し、関連するタイムアウトフラグを設定し、オプションで信号を送信する単一のタイマーを使用する必要があります(空のボディハンドラーを使用して)、目的のスレッドに割り込み、割り込みが発生することを確認します。 (シングルスレッドプロセスの場合、元のシグナル配信により、I/O呼び出しのブロックが中断されます。)サーバーが何らかの要求に応答する場合を考えてみます。要求自体は、要求の処理中に1分程度のタイムアウトになる場合があります。接続タイムアウト、I/Oタイムアウトなどが必要になる場合があります。
タイマーが効果的に使用されると強力になるため、元の質問は興味深いものです。ただし、サンプルプログラムは基本的にナンセンスです。 1つまたは複数のタイマーを設定するプログラムを作成してみませんか。たとえば、それぞれが標準出力に何かを出力しています。 unistd.h
のwrite()
et alは async-signal safe であるため、忘れないでください。stdio.h
のprintf()
et ceteraはそうではありません。 (シグナルハンドラーが非同期シグナルセーフでない関数を使用している場合、結果は定義されていません。通常は機能しますが、まったく保証されません。機能するだけでなくクラッシュする可能性もあります。テストでは通知されませんundefined。)
追加のために編集:多重化タイムアウトの基本的な例を以下に示します。
(法律の下で可能な限り、私は以下に示すコードスニペットのすべての著作権および関連する権利と近隣の権利を世界中のパブリックドメインに捧げます。 CC0 Public Domain Dedication を参照してください。つまり、自由に使用してください以下のコードはどのような方法でも使用できますが、問題が発生しても私を責めないでください。)
私は古いスタイルのGCCアトミック組み込みを使用したので、スレッドセーフである必要があります。いくつか追加すると、マルチスレッドコードでも機能するはずです。 (pthread_mutex_lock()
は非同期シグナルに対して安全ではないため、たとえばmutexを使用することはできません。タイムアウトが発生したときにタイムアウトを無効にすると競合が残る可能性がありますが、タイムアウト状態の原子的な操作は機能するはずです。)
#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()
を呼び出して必要なハンドラーなどをすべてインストールし、終了する前に(またはtimeout_done()
ingの後の子プロセスで)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;
}
役立つ読み物は time(7) のmanページです。 Linuxは timerfd_create(2) Linux固有 syscall も提供していることに注意してください poll(2) (またはppoll(2)
または以前のselect(2)
syscall)。
シグナルを使用したい場合は、注意深く読むことを忘れないでください signal(7) manページ(シグナルハンドラのコーディングには制限があります。volatile sigatomic_t
シグナルハンドラの変数。シグナルハンドラ内でnew
またはdelete
-or malloc
&free
-メモリ管理操作を実行しないでください。ここで、async-safe関数呼び出しが許可されます。).
GUIアプリケーションなどのイベント指向プログラミングは、多くの場合(Gtk、Qt、libevent
、...を使用して)イベントループでタイマーを管理する方法を提供することに注意してください。