В чем проблема с PTHREAD_CANCELED и возвращаемым значением функции запуска потока - PullRequest
0 голосов
/ 23 января 2019

Я читаю книгу Керриска и вижу следующее как примечание:

Необходимо соблюдать осторожность при использовании целого числа приведения в качестве возвращаемого значения функции запуска потока. Причина этого заключается в том, что PTHREAD_CANCELED, значение, возвращаемое при отмене потока (см. Глава 32), обычно это приведение к определенному целочисленному значению до void *. Если функция запуска потока возвращает то же самое целое число затем значение в другой поток, который делает pthread_join(), это будет ошибочно появляться, что поток был отменен. В приложении который использует отмену потока и выбирает вернуть целое число приведения значения из начальных функций потока, мы должны убедиться, что обычно завершающий поток не возвращает целое число, значение которого совпадает PTHREAD_CANCELED в этой реализации Pthreads. портативный приложение должно гарантировать, что нормально завершающие потоки не возвращайте целочисленные значения, которые соответствуют PTHREAD_CANCELED ни на одном из реализации, на которых должно выполняться приложение.

Я не понимаю важности записки. Не могли бы вы систематизировать (показать его простой фрагмент кода) это просто для иллюстрации? В чем проблема в этом (е) случае (ах)?

1 Ответ

0 голосов
/ 24 января 2019

Это типичное определение PTHREAD_CANCELED (дословно цитируется /usr/include/pthread.h на компьютере, на котором я это печатаю, на котором работает Linux с GNU libc):

#define PTHREAD_CANCELED ((void *) -1)

Так что, если у вас есть такой код для проверки отмены:

void *thread_result;
int rv = pthread_join(child, &thread_result);
if (rv)
    error_exit("pthread_join failed", rv);
if (thread_result == PTHREAD_CANCELED)
    error_exit("thread canceled", 0);

у вас не должно быть процедуры потока, подобной этой:

static void *appears_to_be_canceled(void *unused)
{
    return ((void *) -1);
}

потому что PTHREAD_CANCELED и ((void *) -1) равны. Обратите внимание, что число не обязательно должно быть минус 1, оно может отличаться от системы к системе, и нет хорошего способа узнать, что это за время компиляции, потому что ((void *)...) не может использоваться в выражении #if.

Есть два хороших способа избежать этой проблемы:

  • Не используйте отмену потока, поэтому вам не нужно проверять PTHREAD_CANCELED и не беспокоиться о его числовом значении. Это хорошая идея по ряду других причин, наиболее важно то, что отмена делает написание надежного многопоточного кода еще сложнее, чем это уже есть.
  • Возвращать только правильные указатели из ваших потоковых процедур, а не числа. Хорошая идиома для подражания выглядит так:

    struct worker_data
    {
       // put _everything_ your thread needs to access in here
    };
    static void *worker_proc (void *data_)
    {
       struct worker_data *data = data_;
       // do stuff with `data` here 
       return data_;
    }
    

    Возвращение объекта worker_data означает, что коду, вызывающему pthread_join, не нужно отслеживать, какой объект worker_data соответствует какому pthread_t. И это также означает, что возвращаемое значение успешно завершенного потока гарантированно не будет равно PTHREAD_CANCELED, потому что PTHREAD_CANCELED гарантированно не будет сравниваться ни с одним допустимым указателем.

...