pthread_create время обратной записи

#c #pthreads #race-condition

#c #p — потоки #состояние гонки #pthreads

Вопрос:

При вызове pthread_create(amp;id, NULL, amp;start_routine, arg) гарантируется ли, что идентификатор потока будет записан в id перед запуском start_routine? На страницах руководства ясно указано, что start_routine может, но не обязательно начнет выполняться до возврата вызова pthread_create, но они не сообщают, когда идентификатор потока записывается обратно в переданный аргумент потока.

Мой конкретный случай заключается в том, что у меня есть оболочка вокруг pthread_create:

 int mk_thread(pthread_t *id) {
  pthread_t tid;
  pthread_create(amp;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:amp;tid;
  pthread_create(tidPtr,NULL,ThreadStart,NULL);
  if (id == NULL) {
     pthread_detach(tid);
  }
}
  

Эта перезапись намного более стабильна на практике, но на самом ли деле это исправление или просто меньшее окно для условий гонки?

Комментарии:

1. Вы уверены, что второй вариант более стабилен? Я заметил опечатку в строке 2, где tidPtr должен быть указателем, в то время как в нем отсутствует звездочка. Кроме того, что находится внутри переменной «lid»?

2. Я был ленив и ввел это вручную. В реальном коде нет этих опечаток. «Крышка» в первом должна быть «tid», и да, * отсутствует во втором.

3. Не забудьте проверить возвращаемое значение pthread_create , чтобы увидеть, удалось ли это или нет.

Ответ №1:

Идентификатор потока определенно записывается перед pthread_create возвратами. Если вы подумаете об этом, для pthread_create было бы невозможно работать каким-либо другим способом. Не удалось делегировать запись идентификатора потока новому потоку, потому что pthread_t переменная может оказаться вне области видимости к моменту запуска нового потока.

Соответствующий текст является:

После успешного завершения pthread_create() сохранит идентификатор созданного потока в местоположении, на которое ссылается поток.

(Из http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html ) Обратите внимание, что в нем указано «при успешном завершении» функции, а не «через неопределенное время после успешного завершения».

Более интересный вопрос, и я неясен в этом вопросе, заключается в том, pthread_create должен ли быть завершен ввод идентификатора потока в пункт назначения до запуска новой функции запуска потока, т. Е. может ли новый поток сразу увидеть свой собственный идентификатор потока, например, если он должен быть сохранен в глобальной переменной. Я подозреваю, что ответ отрицательный.

Редактировать: При перечитывании вашего вопроса кажется, что вы, возможно, действительно спрашивали об этом последнем, более интересном вопросе. В любом случае, у функции запуска нового потока нет причин использовать идентификатор потока, записанный pthread_create . Ваш новый поток может (и должен) просто использовать pthread_self для получения своего собственного идентификатора потока.

Комментарии:

1. Нет ли функции pthread_self() для просмотра собственного идентификатора потока? Я думаю, что проблема кроется где-то в другом.

2. Условия гонки возможны только между разными потоками, а не внутри одного потока. Таким образом, я полагаю, что вопрос имеет смысл только в том случае, если OP спрашивает о том, будет ли идентификатор потока записан вовремя, чтобы новый поток его увидел. Проблема легко решается, если новый поток просто использует pthread_self для получения своего собственного идентификатора потока.

3. Да, состояние гонки возможно только в разных потоках или процессах. Однако обычно это решается блокировкой мьютексов, а не отслеживанием идентификатора потока. Но, эй, мы не видим коды, в которых возникают условия гонки, я думаю, мне лучше оставить это на некоторое время.

4. pthread_self() здесь не помогает, потому что не новому потоку нужно знать свой собственный идентификатор, а любому запущенному потоку, который может проверить глобальный (да, защищенный мьютексом) список запущенных потоков, чтобы узнать, к каким потокам можно присоединиться.

5. Ах, я наконец-то нашел версию ссылки с информацией об этом конкретном элементе из открытой группы . Точная цитата здесь такова: «В реализации нет требования, чтобы идентификатор созданного потока был доступен до начала выполнения вновь созданного потока. » который отвечает на мой вопрос «безопасно ли это» однозначным «нет». Итак, чтобы быть правильным, моему приложению необходимо получить идентификатор по-другому (например, немедленно перевести процедуру запуска в режим ожидания и получить сигнал после копирования идентификатора обратно)

Ответ №2:

Я полагаю, что ничто в спецификации не требует, чтобы pthread_create назначал свой выходной параметр pthread_t *thread перед тем, как код в start_routine начнет выполняться.

С практической точки зрения, следующая программа успешно работает во многих реализациях 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=%jdn", (intmax_t)th_, (intmax_t)self);
        assert(pthread_equal(th_, self));
}

int main() {
        int i;
        for(i=0; i<1000; i  ) {
                pthread_create(amp;th, NULL, asserter, NULL);
                pthread_join(th, NULL);
        }
        return 0;
}
  

тем не менее, я не уверен, что понимаю, как эта деталь поведения относится к двум альтернативам кода, которые вы предлагаете в исходном вопросе. Хотя мне приходит в голову, что если pthread_create записывает другие значения в *thread во время своего выполнения, и вы используете значение *id в другом потоке, это может иметь значение. Стандарт не указывает, что никакие другие «промежуточные» значения не записываются в *thread во время успешного выполнения pthread_create.