我们在计时器到期时生成SIGALRM的进程中使用posix间隔计时器(使用timer_create()创建)。生成的信号由进程中的特定线程异步处理(sigwait),我们使用sig_block阻塞了所有其他线程中的信号。' Sig_block '在子线程生成之前在主线程中被调用,因此子线程从父线程(即main)继承它。但是,这需要注意,如果进程中包含的任何库在dllmain期间生成任何线程,则信号不会在该线程中被阻塞。此外,我们无法控制包含在流程中的dll的内部实现。你能建议如何处理这个问题吗?是否有其他方法将定时器过期信号定位到进程中的特定线程?
我检查了选项'SIGEV_THREAD_ID'。然而,文档声明它仅供线程库使用。
如果您不介意使用特定于linux的SIGEV_THREAD_ID
。此外,我建议使用实时信号(SIGRTMIN+0
到SIGRTMAX-0
,包括在内),因为这些信号是按发送的顺序排队和交付的。
SIGEV_THREAD_ID
被记录为仅供线程库使用的原因是Linux线程id通常不公开;这个接口不能直接用于例如pthreads。您需要实现自己的gettid()
:
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
static inline pid_t gettid(void) { return syscall(SYS_gettid); }
这将依赖于Linux的pthread不做任何愚蠢的事情,比如在保持相同的pthread_t ID的同时切换线程ID。
我个人建议使用一种不同的方法,使用一个帮助线程来维护超时。
让线程维护与目标线程ID (pthread_t
)相关联的超时时间戳的排序数组或二进制堆。线程将在pthread_cond_timedwait()
中等待,直到下一个超时过期,或者发出信号,表明超时已更改(取消或添加新超时)。当一个或多个超时过期时,线程使用pthread_sigqueue()
向目标线程发送适当的信号,并将超时标识符作为有效负载。
也许一个粗略的简化草图有助于理解。为简单起见,假设挂起的超时形成一个单链表:
struct timeout {
struct timeout *next;
struct timespec when; /* Absolute CLOCK_REALTIME time */
double repeat; /* Refire time in seconds, 0 if single-shot */
pthread_id thread;
int elapsed;
};
pthread_mutex_t timeout_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t timeout_wait = PTHREAD_COND_INITIALIZER;
struct timeout *timeout_pending = NULL;
int timeout_quit = 0;
static inline int timespec_cmp(const struct timespec t1, const struct timespec t2)
{
return (t1.tv_sec < t2.tv_sec) ? -1 :
(t1.tv_sec > t2.tv_sec) ? +1 :
(t1.tv_nsec < t2.tv_nsec) ? -1 :
(t1.tv_nsec > t2.tv_nsec) ? +1 : 0;
}
static inline void timespec_add(struct timespec *const ts, const double seconds)
{
if (seconds > 0.0) {
ts->tv_sec += (long)seconds;
ts->tv_nsec += (long)(1000000000.0*(double)(seconds - (long)seconds));
if (ts->tv_nsec < 0)
ts->tv_nsec = 0;
if (ts->tv_nsec >= 1000000000) {
ts->tv_sec += ts->tv_nsec / 1000000000;
ts->tv_nsec = ts->tv_nsec % 1000000000;
}
}
}
struct timeout *timeout_arm(double seconds, double repeat)
{
struct timeout *mark;
mark = malloc(sizeof (timeout));
if (!mark) {
errno = ENOMEM;
return NULL;
}
mark->thread = pthread_self();
mark->elapsed = 0;
clock_gettime(CLOCK_REALTIME, &(mark->when));
timespec_add(&(mark->when), seconds);
mark->repeat = repeat;
pthread_mutex_lock(&timeout_lock);
mark->next = timeout_pending;
timeout_pending = mark;
pthread_cond_signal(&timeout_wait);
pthread_mutex_unlock(&timeout_lock);
return mark;
调用timeout_arm()
返回一个指向超时的指针作为标识符,以便线程可以稍后解除它:
int timeout_disarm(struct timeout *mark)
{
int result = -1;
pthread_mutex_lock(&timeout_lock);
if (timeout_pending == mark) {
timeout_pending = mark->next;
mark->next = NULL;
result = mark->elapsed;
} else {
struct timeout *list = timeout_pending;
for (; list->next != NULL; list = list->next) {
if (list->next == mark) {
list->next = mark->next;
mark->next = NULL;
result = mark->elapsed;
break;
}
}
}
/* if (result != -1) free(mark); */
pthread_mutex_unlock(&timeout_lock);
return result;
}
请注意,上面的函数没有free()
超时结构(除非您取消末尾附近的行注释),如果找不到超时,它返回-1
,如果成功,则返回超时移除时的elapsed
字段。
管理超时的线程函数相当简单:
void *timeout_worker(void *unused)
{
struct timespec when, now;
struct timeout *list;
pthread_mutex_lock(&timeout_lock);
while (!timeout_quit) {
clock_gettime(CLOCK_REALTIME, &now);
/* Let's limit sleeps to, say, one minute in length. */
when = now;
when.tv_sec += 60;
/* Act upon all elapsed timeouts. */
for (list = timeout_pending; list != NULL; list = list->next) {
if (timespec_cmp(now, list->when) >= 0) {
if (!list->elapsed || list->repeat > 0) {
const union sigval value = { .sival_ptr = list };
list->elapsed++;
pthread_sigqueue(list->thread, TIMEOUT_SIGNAL, value);
timespec_add(&(list->when), list->repeat);
}
} else
if (timespec_cmp(when, list->when) < 0) {
when = list->when;
}
}
pthread_cond_timedwait(&timeout_wait, &timeout_lock, &when);
}
/* TODO: Clean up timeouts_pending list. */
return NULL;
}
注意,我没有检查上面的错字,所以可能会有一些。上面所有的代码都是在CC0-1.0下授权的:你想做什么就做什么,只是不要因为任何错误而责怪我。
不幸的是,您所希望的将计时器信号定向到特定线程的行为是不可移植的。
工作围绕您的DLL的naïve行为-所以naïve我认为它有bug -你有几个便携式的选项。
在调用exec
之前,您可以调用已经阻塞了SIGALRM的程序。
你的计时器可以指定SIGEV_THREAD,然后该线程可以处理超时或通知你的专用线程该工作了。
您可以在同步睡眠线程中实现自己的时间保持,而不需要信号,如Glärbo所建议的。