pthread_create 写回时间

Posted

技术标签:

【中文标题】pthread_create 写回时间【英文标题】:pthread_create timing of writeback 【发布时间】:2011-06-14 03:30:10 【问题描述】:

在调用pthread_create(&id, NULL, &start_routine, arg)中,线程id是否保证在start_routine开始运行前写入id?手册页清楚地表明 start_routine 可能但不一定会在 pthread_create 调用返回之前开始执行,但是当线程 id 被写回传递的线程参数时,它们是静默的。

我的具体情况是我有一个 pthread_create 的包装器:

int mk_thread(pthread_t *id) 
  pthread_t tid;
  pthread_create(&tid,NULL,ThreadStart,NULL);
  if (id == NULL) 
    pthread_detach(tid);
   else 
    *id=lid;
  

这显然可以在回写之前运行启动例程。我把它改成

int mk_thread(pthread_t *id) 
  pthread_t tid,tidPtr=id?id:&tid;
  pthread_create(tidPtr,NULL,ThreadStart,NULL);
  if (id == NULL) 
     pthread_detach(tid);
  

这种重写在实践中更加稳定,但它实际上是一个修复还是只是一个较小的竞争条件窗口?

【问题讨论】:

你确定第二个更稳定吗?我注意到第 2 行有一个错字,其中 tidPtr 应该是一个指针,而它缺少一个星号。另外,变量“lid”里面是什么? 我很懒,用手打字。真正的代码没有那些错别字。第一个中的“盖子”应该是“tid”,是的,第二个中缺少 *。 别忘了检查pthread_create的返回值看看是否成功。 【参考方案1】:

线程id肯定是在pthread_create返回之前写的。如果您考虑一下,pthread_create 不可能以任何其他方式工作。它无法将线程 ID 的写入委托给新线程,因为在新线程运行时,pthread_t 变量可能已超出范围。

相关文字为:

成功完成后,pthread_create() 将创建线程的 ID 存储在线程引用的位置。

(来自http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html)请注意,它说的是“成功完成”功能,而不是“在成功完成后的不确定时间”。

更有趣的问题是pthread_create是否必须在新线程启动函数开始之前完成将线程ID写入其目的地,即新线程是否可以立即看到自己的线程 ID,例如如果要存储在全局变量中。我怀疑答案是否定的。

编辑:重读您的问题后,您似乎真的一直在问后一个更有趣的问题。无论如何,新线程的启动函数没有理由使用pthread_create 写出的线程ID。您的新线程可以(并且应该)只使用 pthread_self 来获取自己的线程 ID。

【讨论】:

难道没有pthread_self()可以查看线程自己的ID吗?我认为问题出在其他地方。 竞争条件只能在不同的线程之间,而不是在单个线程内。所以我相信,这个问题只有在 OP 询问是否会及时写入线程 ID 以便新线程看到它时才有意义。这个问题很容易解决,让新线程只需使用pthread_self 来获取它自己的线程ID。 是的,竞争条件只能在不同的线程或进程上出现。但是,它通常通过锁定互斥锁而不是跟踪线程的 ID 来解决。但是,嘿,我们看不到发生竞争条件的代码,我想我还是暂时留下吧。 pthread_self() 在这里没有帮助,因为它不是需要知道自己的 id 的新线程,而是任何可能检查运行的全局(是的,互斥保护的)列表的正在运行的线程线程知道可以加入哪些线程。 啊,我终于从the open group 中找到了包含此特定项目信息的参考版本。这里的确切引用是“在新创建的线程开始执行之前,对实现没有要求创建线程的 ID 可用。”这回答了我的“它是否安全”的问题,肯定没有。所以正确地说,我的应用程序需要以不同的方式获取 id(例如让启动例程立即休眠并在 id 被复制回来后收到信号)【参考方案2】:

我相信规范中的任何内容都不需要 pthread_create 在start_routine 中的代码开始执行之前分配其输出参数pthread_t *thread

出于实用性的考虑,以下程序在许多 pthreads 实现(freebsd8 i386 和 debian gnu/linux amd64)上成功,但在我感兴趣的一个(debian/kfreebsd9 amd64)上失败:

#include <pthread.h>
#include <assert.h>
#include <stdint.h>
#include <stdio.h>

pthread_t th;

void *asserter(void* unused) 
        pthread_t self = pthread_self(), th_=th;
        printf("th=%jd self=%jd\n", (intmax_t)th_, (intmax_t)self);
        assert(pthread_equal(th_, self));


int main() 
        int i;
        for(i=0; i<1000; i++) 
                pthread_create(&th, NULL, asserter, NULL);
                pthread_join(th, NULL);
        
        return 0;

也就是说,我不确定我是否理解这种行为细节与您在原始问题中提供的两种代码替代方案有何关联。虽然我想到如果 pthread_create 在执行期间将其他值写入*thread,并且您在另一个线程中使用*id 的值,它可能是相关的。该标准未指定在成功执行 pthread_create 期间不会将其他“中间”值写入*thread

【讨论】:

以上是关于pthread_create 写回时间的主要内容,如果未能解决你的问题,请参考以下文章

如何实现线程互等,线程2等待线程1结束后才继续执行。(可设置标志位) 求源代码

如何将pthread_t映射到pid(在Linux上)

如何在 pthread_create() 函数中将矩阵作为参数传递?

pthread_create() 不正确的启动例程参数传递

pthread_create() 和内存泄漏

除了 pthread_create 在 linux 上创建 linux 线程的方法