pthread_kill()包含无效的线程
我想确定一个特定的线程是否“存在”。
pthread_kill()
似乎适合此任务,至少根据其手册页 。
如果sig为0,则不发送任何信号,但仍会执行错误检查。
或者,正如我的系统的手册页所示:
如果sig为0,则不发送信号,但仍然执行错误检查; 这可以用于检查是否存在线程ID。
但是,当我尝试传入未初始化的pthread_t
,应用程序总是SEGFAULTs。
深入研究,来自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。 在可以检测到无效线程ID的情况下,glibc实现返回此错误。 但请注意,POSIX表示尝试使用其生命周期已结束的线程ID会产生未定义的行为 ,并且在调用pthread_kill()时尝试使用无效的线程ID可能会导致分段错误。
正如R ..所述 ,我要求可怕的未定义行为 。
看来该手册确实具有误导性 – 特别是在我的系统上。
- 是否有一种好的/可靠的方法来询问是否存在线程? (大概是不使用
pthread_kill()
) - 是否有一个很好的值可用于初始化
pthread_t
类型变量,即使我们必须自己捕获它们?
我怀疑答案是使用pthread_cleanup_push()
并保留我自己的is_running
标志,但是想听别人的想法。
我想我在开车回家的时候已经意识到了,我怀疑很多其他人也觉得这很有用……
看起来我一直在把工人(线程)和任务(线程正在做什么)视为同一个,而事实上,他们不是。
正如我已经从问题中的代码片段建立的那样,因为pthread_t
可能只是一个指针(它肯定在我的目标上),因此询问“ 这个线程是否存在 ”是不合理的。 这几乎肯定是错误的问题。
进程ID,文件句柄, malloc()
内存等也是如此……它们不使用唯一且永不重复的标识符,因此不是可以测试它们存在的唯一“实体”。
我在问题中提出的怀疑可能是正确的 – 我将不得不使用像is_running
标志这样的任务 (而不是线程)。
我想到的一种方法是使用初始化为one, sem_trywait()
, sem_post()
和pthread_cleanup_push()
的信号量,如下例所示(为简洁起见,缺少清理)。
#include #include #include #include #include #include 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!