#c #multithreading #pthreads #pthread-join
#c #многопоточность #p — потоки #pthread-join
Вопрос:
Я только начал изучать многопоточность на C с помощью pthreads. Я работаю со следующим кодом:
struct ArgT{
int a;
int b;
ArgT(int a, int b)
: a(a), b(b)
{}
ArgT()
:a(0), b(0)
{}
};
void* worker(void* arg)
{
ArgT* myArg = (ArgT*) arg;
int* result = new int;
*result = myArg->a myArg->b;
std::cout << "(void*)result: " << (void*) result << std::endl;
return (void*)resu<
}
int main()
{
ArgT mainArg(2,3);
pthread_t p;
int* main_resu<
pthread_create(amp;p, NULL, worker, (void*)amp;mainArg);
pthread_join(p, (void**)amp;main_result); //??
std::cout << "main_result: " << main_result << std::endl;
std::cout << "amp;main_result: "<< amp;main_result << std::endl;
printf("nResult = %dn", *main_result);
delete main_resu<
return 0;
}
Вывод кода выглядит следующим образом
(void*)result: 0x7f07ac0008c0
main_result: 0x7f07ac0008c0
amp;main_result: 0x7fffb1aa0588
Result = 5
Мой вопрос pthread_join()
принимается void**
в качестве второго аргумента, который в основном является адресом адреса. Принимая во внимание, что мы возвращаем адрес типа void*
в worker()
функции. Как совместимы эти два типа?
Комментарии:
1. Поскольку вы программируете на C , я действительно рекомендую вам
std::thread
вместо этого заглянуть внутрь. Или, если вы ожидаете возврата результатаstd::async
.2. Что касается
void**
проблемы, это способ эмулировать передачу по ссылке в C (API потоков POSIX — это C API).3. Спасибо за информацию. Это мой следующий вопрос: в чем основное преимущество использования std::thread вместо pthreads. Я в основном придерживаюсь pthreads, потому что книга, которую я сейчас читаю (Операционные системы: три простых фрагмента), объяснила все с помощью pthread @Someprogrammerdude
4. Помимо того, что он является частью стандартной библиотеки C и, следовательно, переносим? Ну, обычно это намного проще. Вы могли бы просто иметь
void worker(ArgT argument) { ... }
, а затем создать поток, подобныйstd::thread my_thread(amp;worker, mainArg); /* Other code... */ my_thread.join();
No more casting, типобезопасный, объектно-ориентированный, более простой … 😉5. Я понимаю. Я думаю , что буду придерживаться pthreads , пока не закончу эту книгу , и , возможно, позже я смогу переключиться на
std::threads
. В основном я хочу изучить теорию многопоточности. Я думаю, что как только я буду достаточно компетентен в теории, я смогу переключать API. Спасибо за ваш вклад. @Someprogrammerdude
Ответ №1:
pthread_join()
принимает адрес переменной указателя. Эта переменная получит значение указателя, возвращаемое потоком. Он фактически делает то же самое, что и этот:
void doIt(void (*f)(), void** ptr)
{
*ptr = f();
}
void* func()
{
return ...;
}
int main()
{
void *ptr;
doIt(func, amp;ptr);
// ptr holds whatever func() returns...
}
Ответ №2:
Как совместимы эти два типа?
При void**
косвенном обращении через результатом является значение lvalue типа void*
Ваша программа имеет неопределенное поведение. Вот правильный способ:
void* void_main_resu<
pthread_join(p, amp;void_main_result);
int* int_main_result = static_cast<int*>(void_main_result);
std::cout << "nResult = " << *int_main_resu<
Вы можете избежать динамического распределения, выделив результат внутри аргументов. Таким образом, возвращаемое значение не требуется:
struct ArgRet {
ArgT args;
int ret;
} mainArg;
pthread_create(amp;p, NULL, worker, amp;mainArg);
std::cout << "nResult = " << mainArg.ret;
// in worker
ArgRet* myArg = static_cast<ArgRet*>(arg);
myArg->ret = myArg->args.a myArg->args.b;
return arg; // or nullptr; doesn't really matter
в чем основное преимущество использования std::thread вместо pthreads
std::thread
работает во всех реализациях C (11 или более поздней версии). pthreads работают только в системах POSIX. API of std::thread
также проще в использовании, поскольку он написан на C , а не на C.
P.S. Преобразование указателя в void*
неявно. Я рекомендую не использовать приведения в стиле C.
Комментарии:
1. Спасибо за ваш ответ. Можете ли вы сказать мне, что, по вашему мнению, вызывает неопределенное поведение? Книга, которую я читаю в настоящее время, имела ту же реализацию, и вывод кода также, похоже, в порядке.
2. @HariP19 Это UB, потому
pthread_join
что будет косвенным черезvoid**
и присваивать значение lvalue, и в этом случае применяется это стандартное правило:If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined: - the dynamic type of the object, ...
Динамический тип объекта равенint*
и значение lvalue, которое используется для доступа к нему внутриpthread_join
, равноvoid*
. Это не тот же тип. В правиле есть список других разрешенных типов, ни один из которых не применяется в данном случае. Похоже, в книге есть ошибка.