Как получить код ошибки из pthread_join()?

В следующем коде не удается присоединиться к pthreads, и выводится сообщение об ошибке присоединения. Как получить дополнительную информацию о сбое и его причине?

pthread_t aThread[MAX_LENGTH];
    int errCode[MAX_LENGTH];
    char returnVal;    
for(int i = 0; i < MAX_LENGTH; i++)
    {

        if((errCode[i] = pthread_create(&aThread[i], NULL, &findMatch, &fpArgs)) != 0)
            printf("error creating thread %d\n", errCode[i]);
        if(!pthread_join(aThread[i], (void**)&returnVal))
            printf("join failed\n i is %d", i);
    }

EDIT: на самом деле join вернул no error, и я допустил ошибку. Статус if не должен иметь !, потому что соединение возвращает ненулевое число, если есть проблема, которая оценивается как истинная.


person Celeritas    schedule 10.10.2013    source источник
comment
Преобразование вашего адреса returnVal в адрес void** является неопределенным поведением. Если только sizeof(char) == sizeof(void*) в вашей системе (я могу почти гарантировать вам, что это нет), не похоже, что вы все равно используете его, и NULL является приемлемым вариантом, поэтому вы можете просто pthread_join(aThread[i], NULL); Кроме того, pthread_join возвращает ноль (0) в случае успеха, поэтому проверка на наличие ошибки с помощью ! выполняется в обратном порядке. Наконец, вы все равно не хотите делать это таким образом, вы хотите начать их все, затем присоединиться к ним всем. То, что у вас есть, буквально не лучше однопоточного.   -  person WhozCraig    schedule 10.10.2013


Ответы (5)


Я указал на это в комментарии, но это заслуживает усиления.

Ваше использование returnVal неверно

API pthread_join ожидает void**, то есть указатель на void*. В отличие от void*, void** не столь универсален. Это указатель определенного типа, и поэтому вы должны передавать только такой же тип адреса. Однако вы все равно его не используете, поэтому я бы посоветовал вам просто передать NULL. Как написано, это неопределенное поведение. И я могу почти гарантировать вам, что sizeof(char), доступный для записи размер адреса, который вы ему даете, и sizeof(void*), размер, который он ожидает получить, различны. Рассмотрим это вместо этого сейчас:

pthread_join(aThread[i], NULL);

Если вам интересно, как использовать этот параметр void**, это место для хранения void* возвращаемого значения из вашего потока-процедуры. Вспомните, как pthread thread-proc выглядит так:

void* thread_proc(void* args)
// ^----- this is what is stashed in the pthread_join second parameter

Вы считаете, что логика тестирования отказов обратная

Функция pthread_join возвращает 0 в случае успешного выполнения; не на провал.


На самом деле вы не запускаете параллельные потоки

Параллелизм потоков просто означает, что ваши потоки выполняются одновременно. Но у вас нет. Вы запускаете поток, затем ждете его завершения, затем запускаете поток, затем ждете его завершения и т. д. Это буквально не лучше (и на самом деле хуже), чем простой вызов функция. Если вы хотите, чтобы ваши потоки выполнялись одновременно, ваша логика должна быть оформлена следующим образом:

pthread_t aThread[MAX_LENGTH];
int errCode[MAX_LENGTH] = {0};

for (int i = 0; i < MAX_LENGTH; i++)
{
    if((errCode[i] = pthread_create(&aThread[i], NULL, &findMatch, &fpArgs)) != 0)
        printf("error creating thread %d, error=%d\n", i, errCode[i]);
}

for (int i = 0; i < MAX_LENGTH; i++)
{
    // note the check for errCode[i], which is only non-zero 
    //  if the i'th thread failed to start
    if(errCode[i] == 0)
    {
        errCode[i] = pthread_join(aThread[i], NULL))
        if (errCode[i] != 0)
            printf("error joining thread %d, error=%d\n", i, errCode[i]);
    }
}
person WhozCraig    schedule 10.10.2013

В случае сбоя функции (т. е. при любом вызове pthread код возврата не равен нулю) она установит errno в значение причины сбоя. Есть несколько способов получить текстовое объяснение кода ошибки.

int returnval;

if((returnval = pthread_join(aThread[i], (void**)&returnVal)) != 0)
{
    printf("error joining thread: %s\n", strerror(returnval));  //1st optiop

    perror("error joining thread:");  //2nd option

    printf("error joining thread: %m\n");  //3rd option

}

(1) strerror напечатает строку ошибки переданного вами значения ошибки и удобна для размещения в операторах printf.

(2) perror позволяет вам передать небольшую строку, которая сначала будет напечатана, а затем автоматически распечатает описание ошибки для любого значения, установленного для errno. Вам не нужно явно передавать errno.

(3) Существует расширение glibc для printf, предоставляющее спецификатор преобразования %m, который действует как strerror, но с меньшим количеством шума и суеты. Это будет наименее портативным.

Как только вы получите описание, вы можете легко заглянуть в справочные страницы вызова, который не удался, и они предоставят более подробные подсказки относительно того, почему вызов не удался. Чарли Бернс опубликовал причины, по которым pthread_join может выйти из строя.

person Duck    schedule 10.10.2013
comment
Вы можете назначить возвращаемое значение вашего вызова от pthread_join к errno, но я не думаю, что библиотека pthread сделает это автоматически за вас. Так что ваш первый абзац неверен. Вы можете использовать returnval так, как это было errno, чтобы остальная часть вашего ответа выглядела правильно. (-1) - person mihai; 16.07.2016

Я что-то упускаю? Возвращаемое значение сообщает вам об ошибке:

ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ В случае успеха функция pthread_join() вернет ноль. В противном случае будет возвращен номер ошибки, указывающий на ошибку.

ОШИБКИ pthread_join() завершится ошибкой, если:

 [EDEADLK]          A deadlock was detected or the value of thread speci-
                    fies the calling thread.

 [EINVAL]           The implementation has detected that the value speci-
                    fied by thread does not refer to a joinable thread.

 [ESRCH]            No thread could be found corresponding to that speci-
                    fied by the given thread ID, thread.
person Charlie Burns    schedule 10.10.2013
comment
Как заставить его распечатать, какой из них это? Например, joinErr = pthread_join(aThread[i], &returnVal); printf("join returned %d\n", joinErr); всегда равно 0 - person Celeritas; 10.10.2013
comment
@Celeritas всегда ноль означает, что он успешен; не провалился. - person WhozCraig; 10.10.2013

Более конкретно::

int retVal = pthread_create(&myThread, NULL,myThreadFn, NULL);
printf("error joining thread: %d\n", retVal);
person parasrish    schedule 28.07.2015

Библиотека pthread не устанавливает переменную errno в случае ошибки. Вместо этого функция возвращает код ошибки. Онлайн-руководство по Linux достаточно ясно описывает функции pthread (например, man pthread_join), так как раздел ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ обычно содержит что-то вроде:

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

В случае успеха pthread_join() возвращает 0; при ошибке возвращает номер ошибки.

Если вам нужно вывести ошибку с помощью таких функций, как strerror(), strerror_r() или %m в формате printf (последнее является расширением GLIBC), вы должны использовать код возврата неисправной службы или обновить errno в ветке ошибок :

if ((rc = pthread_join(...)) != 0) {
  errno = rc;
  fprintf(stderr, "pthread_join(): %m\n");
 OR
  fprintf(stderr, "pthread_join(): %m\n", strerror(errno)); // rc could be used without errno
 OR
  char err_buf[128];
  errno = rc;
  fprintf(stderr, "pthread_join(): %m\n", strerror_r(errno, err_buf, sizeof(err_buf))); // rc could be used without errno

Примечания:

  • errno является потокобезопасным (он находится в локальном хранилище потока). Таким образом, это локально для каждого потока
  • strerror_r() и %m следует использовать в многопоточной среде, так как они потокобезопасны (strerror() — нет).
person Rachid K.    schedule 03.11.2020