#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.