2012-01-02 2 views
5

У меня есть поток под названием mainloopКак обнаружить ранний выход из pthread_create без блокировки слишком долго?

т.е.

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

Поскольку вы используете Linux, вы можете использовать 'pthread_tryjoin_np', но' np' здесь означает «не переносимый»! – fge

+0

fge, все будет хорошо, так как это реализация только linux. Вы хотите предложить это как ответ и, возможно, предложить альтернативу? – Matt

ответ

4

Использование pthread_tryjoin_np было бы неверным: новый поток мог быть произвольно задерживаться между возвратом pthread_create и новым потоком, фактически выполняющим код инициализации.

Если вы в течение этой задержки pthread_tryjoin_np, соединение не удастся, и вы решите, что все «хорошо», а на самом деле это не так.

Что вы хотите, это состояние: client_open будет ожидать на нем, и mainloop будет сигнализировать об этом (после выполнения инициализации).

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); 
} 
-1

Хорошо, я обнаружил три способа сделать это.

1) Инициализируйте и передайте переменные mainloop перед запуском.

2) Используйте Linux конкретного pthread_tryjoin_np() или pthread_timedjoin_np() Я думаю, приуроченные присоединиться версия является более подходящей в данном случае, поскольку это позволяет время поток должны быть создан и для инициализации должна быть сделана. Таймаут не должен быть длинным, поэтому он не будет блокировать вызывающего абонента client_open() очень долго.

Однако, как указано @fge, они не переносятся. Хотя это не слишком большая проблема, я думал об альтернативном способе, который заключается в этом.


EDIT: Не такое хорошее решение, но оставил здесь для справки. Лучше было бы сигнализировать об открытии с использованием переменной условия, чтобы инициализация была в порядке.

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; 
} 
+0

Ваше третье решение эквивалентно второму и страдает от одной и той же расы: что, если поток mainloop задерживается более чем на 100 микросекунд? –

+0

Идите с 3-й опцией, однако используйте переменную условия, которую вы всегда устанавливаете вместо 'usleep'. Затем в 'client_open' дождитесь, когда будет задана переменная условия, а затем проверьте, является ли' run_mainloop' 1 или 0. В основном, что предложил @EmployedRussian. – ChrisWue

+0

На самом деле первое решение кажется самым чистым из всех – fge

Смежные вопросы