如何检测pthread_create提前退出而不会阻塞太长时间?

时间:2012-01-02 22:03:34

标签: c linux

我有一个名为mainloop

的帖子

int run_mainloop;

void* mainloop(void* param)
{
    // local vars
    // initialize local vars

    while(run_mainloop)
    {
       // run mainloop
    }

    return 0;
}

线程从名为client_open的函数开始,即

int client_open()
{
    run_mainloop = 1;
    return pthread_create(&thread, NULL, mainloop, NULL);     
}

但是,在mainloop中,如果初始化局部变量失败,我需要立即通知client_open提前退出。

pthread_join不合适,因为它会阻止,我无法client_open阻止。 如果要在返回之前等待一小段时间就可以了。

如果不使用会阻塞的pthread_join,我怎么能以一种很好的方式做到这一点。 我希望能够获得返回码。

3 个答案:

答案 0 :(得分:4)

使用pthread_tryjoin_np是不正确的:新线程可以在pthread_create返回和新线程实际执行初始化代码之间任意延迟。

如果您在此延迟期间pthread_tryjoin_np,则加入将失败,您将确定所有内容都是“正常”,而实际上并非如此。

你想要的是一个条件:client_open将等待它,mainloop将发出信号(完成初始化后)。

答案 1 :(得分:4)

您可以使用称为completion variables的内容。

使用哪个线程可以等到新创建的线程完成初始化。唯一的问题是,即使初始化失败,新线程也必须始终发出初始化完成信号。

以下几行(为清晰起见,省略了错误处理):

#include <pthread.h>

// Completion variable definition:    
typedef struct {
    pthread_mutex_t mtx;
    pthread_cond_t cnd;
    int completed;
    int return_code;
} Completion;

#define COMPLETION_INIT { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0, 0 }

int completion_wait(Completion* c) { // add timeout if necessary
    pthread_mutex_lock(&c->mtx);
    while(!c->completed)
        pthread_cond_wait(&c->cnd, &c->mtx);
    int return_code = c->return_code;
    pthread_mutex_unlock(&c->mtx);
    return return_code;
}

void completion_signal(Completion* c, int return_code) {
    pthread_mutex_lock(&c->mtx);
    c->completed = 1;
    c->return_code = return_code;
    pthread_cond_signal(&c->cnd);
    pthread_mutex_unlock(&c->mtx);
}

// Usage:    

void* mainloop(void* vc) {
    int init_success = 0;
    // initialization
    // ...
    init_success = 1;

init_end:
    Completion* c = (Completion*)vc;
    completion_signal(c, init_success); // always signal
    if(!init_success)
        return NULL;

    // start the main loop
    return NULL;
}

int client_open()
{
    int run_mainloop = 1;
    pthread_t thread;
    Completion c = COMPLETION_INIT;
    pthread_create(&thread, NULL, mainloop, &c);
    pthread_detach(thread);
    return completion_wait(&c);
}

答案 2 :(得分:-1)

好的,我发现了三种方法。

1)在启动之前初始化并将变量传递给mainloop。

2)使用Linux特定的pthread_tryjoin_np()或pthread_timedjoin_np() 我认为定时连接版本在这种情况下更合适,因为它允许创建线程的时间和完成初始化。超时不需要很长,因此它不会将调用者阻塞到client_open()很长时间。

然而,正如@fge指出的那样,它们是不可移植的。虽然这不是一个太大的问题,但我想到了另一种方法。


编辑:不是一个很好的解决方案,但留在这里供参考。 最好使用初始化正常的条件变量来打开信号。

3)检查run_mainloop是否为非零,如果是,并且pthread_create没有失败且线程正在运行。如果它在一段时间后仍然为零,那么它没有启动,所以我们调用pthread_join来获取退出代码。

int run_mainloop = 0;

void* mainloop(void* param)
{
    // init vars
    // if failure, exit early.

    // everything from this point on is good.
    run_mainloop = 1;
    while (run_mainloop))
    {
        // do styff
    }

    return 0;
}

int client_open()
{
    void* res;
    int rc = pthread_create(&g_thread_id, NULL, mainloop, NULL);
    if (rc != 0)
        return -1;

    usleep(100); // wait a little while, kinda dumb but allows time for init
    if (run_mainloop))
        return 0;

    pthread_join(g_thread_id, &res);
    return -1;
}