我想确定特定线程是否"存在"。
pthread_kill()
似乎适合这项任务,至少根据其手册页是这样。
如果 sig 为 0,则不发送信号,但仍执行错误检查。
或者,正如我的系统手册页所说:
如果 sig 为 0,则不发送任何信号,但仍执行错误检查;这可用于检查线程 ID 是否存在。
但是,当我尝试传入未初始化的pthread_t
时,应用程序总是SEGFAULT。
深入研究这一点,来自pthread_kill.c
(来自我的工具链)的以下代码片段似乎没有进行错误检查,只是尝试取消引用threadid
(取消引用位于pd->tid
)。
int
__pthread_kill (threadid, signo)
pthread_t threadid;
int signo;
{
struct pthread *pd = (struct pthread *) threadid;
/* Make sure the descriptor is valid. */
if (DEBUGGING_P && INVALID_TD_P (pd))
/* Not a valid thread handle. */
return ESRCH;
/* Force load of pd->tid into local variable or register. Otherwise
if a thread exits between ESRCH test and tgkill, we might return
EINVAL, because pd->tid would be cleared by the kernel. */
pid_t tid = atomic_forced_read (pd->tid);
if (__builtin_expect (tid <= 0, 0))
/* Not a valid thread handle. */
return ESRCH;
我们甚至不能依赖零是一个好的初始值设定项,因为以下几点:
# define DEBUGGING_P 0
/* Simplified test. This will not catch all invalid descriptors but
is better than nothing. And if the test triggers the thread
descriptor is guaranteed to be invalid. */
# define INVALID_TD_P(pd) __builtin_expect ((pd)->tid <= 0, 0)
此外,我在链接的手册页中注意到以下内容(但不在我的系统上):
POSIX.1-2008 建议,如果实现在其生存期结束后检测到线程 ID 的使用,则 pthread_kill() 应返回错误 ESRCH。 glibc 实现在可以检测到无效线程 ID 的情况下返回此错误。但还要注意的是,POSIX 表示尝试使用生存期已结束的线程 ID 会产生未定义的行为,例如,尝试在调用 pthread_kill() 时使用无效的线程 ID 可能会导致分段错误。
正如R..在这里概述的那样,我要求可怕的未定义行为。
看起来该手册确实具有误导性 - 尤其是在我的系统上。
- 有没有一种好的/可靠的方法来询问线程是否存在?(大概是不用
pthread_kill()
) - 是否有一个好的值可以用来初始化
pthread_t
类型变量,即使我们必须自己捕获它们?
我怀疑答案是雇用pthread_cleanup_push()
并保留自己的is_running
旗帜,但想听听别人的想法。
我想我在开车回家时已经意识到了这一点,我怀疑许多其他人也可能发现这很有用......
看起来我一直在将工作线程(线程)和任务(线程正在做什么)视为一回事,而实际上并非如此。
正如我已经从问题中的代码片段中确定的那样,问">这个线程是否存在"是不合理的,因为pthread_t
可能只是一个指针(它肯定在我的目标上)。这几乎可以肯定是错误的问题。
进程 ID、文件句柄、malloc()
内存等也是如此......它们不使用唯一且从不重复的标识符,因此不是可以测试其存在的唯一"实体"。
我在问题中提出的怀疑可能是真的 - 我将不得不为任务(而不是线程)使用类似is_running
标志的东西。
我想到的一种方法是使用初始化为一个的信号量,sem_trywait()
,sem_post()
和pthread_cleanup_push()
,如下例所示(为简洁起见,缺少清理)。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
struct my_task {
sem_t can_start;
pthread_t tid;
/* task-related stuff */
};
void *my_task_worker(void *arg) {
struct my_task *task = arg;
pthread_cleanup_push(sem_post, &(task->can_start));
fprintf(stderr, "--- task starting!n");
usleep(2500000);
fprintf(stderr, "--- task ending!n");
pthread_cleanup_pop(1);
return NULL;
}
void my_task_start(struct my_task *task) {
int ret;
ret = sem_trywait(&(task->can_start));
if (ret != 0) {
if (errno != EAGAIN) {
perror("sem_trywait()");
exit(1);
}
fprintf(stderr, ">>> task already running...n");
return;
}
ret = pthread_create(&(task->tid), NULL, my_task_worker, task);
if (ret != 0) {
perror("pthread_create()");
exit(1);
}
fprintf(stderr, ">>> started task!n");
return;
}
int main(int argc, char *argv[]) {
int ret;
struct my_task task;
int i;
memset(&task, 0, sizeof(0));
ret = sem_init(&(task.can_start), 0, 1);
if (ret != 0)
{
perror("sem_init()");
return 1;
}
for (i = 0; i < 10; i++) {
my_task_start(&task);
sleep(1);
}
return 0;
}
输出:
>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!