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!