Возврат указателя void в pthread

#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* . Это не тот же тип. В правиле есть список других разрешенных типов, ни один из которых не применяется в данном случае. Похоже, в книге есть ошибка.