Подтвердить что ты не робот

С++: простое возвращаемое значение из std:: thread?

С потоками win32 у меня есть прямой GetExitCodeThread(), который дает мне значение, возвращаемое функцией потока. Я ищу что-то подобное для std::thread (или увеличить потоки)
Насколько я понимаю, это можно сделать с помощью фьючерсов, но как именно?

4b9b3361

Ответ 1

Смотрите этот видеоурок на фьючерсах на С++ 11.

Явно с потоками и фьючерсами:

#include <thread>
#include <future>

void func(std::promise<int> && p) {
    p.set_value(1);
}

std::promise<int> p;
auto f = p.get_future();
std::thread t(&func, std::move(p));
t.join();
int i = f.get();

Или с помощью std::async (обертка более высокого уровня для потоков и фьючерсов):

#include <thread>
#include <future>
int func() { return 1; }
std::future<int> ret = std::async(&func);
int i = ret.get();

Я не могу прокомментировать, работает ли он на всех платформах (похоже, он работает в Linux, но не работает для меня на Mac OSX с GCC 4.6.1).

Ответ 2

Я бы сказал:

#include <thread>
#include <future>

int simplefunc(std::string a)
{ 
    return a.size();
}

int main()
{
      auto future = std::async(simplefunc, "hello world");
      int simple = future.get();

      return simple;
}

Обратите внимание, что async даже распространяет любые исключения, вызванные функцией потока

Ответ 3

Используя потоки С++ 11, невозможно получить возвращаемое значение как выход потока, который был в случае с pthread_exit(...)

Вам нужно использовать С++ 11 Future<> чтобы получить возвращаемое значение. Будущее создается с использованием шаблонного аргумента, где шаблон принимает возвращаемое значение (встроенный в определяемые пользователем типы).

Вы можете получить значение в другом потоке, используя future<..>::get(..) функцию future<..>::get(..).

одно из преимуществ использования future<..> состоит в том, что вы можете проверить правильность возвращаемого значения, т.е. если оно уже принято, вы избегаете случайного вызова get(), проверяя правильность с помощью future<..>::isValid(...) функция.

Вот как вы будете писать код.

#include <iostream>
#include <future>
using namespace std;
auto retFn() {
    return 100;
}
int main() {
    future<int> fp = async(launch::async, retFn);
    if(fp.valid())
       cout<<"Return value from async thread is => "<<fp.get()<<endl;
    return 0;
}

Также следует отметить, что мы можем запустить в будущем тот же поток, используя опцию launch::deferred как

 future<int> fp = async(launch::deferred, retFn);

Ответ 4

Передать ссылку/указатель на поток с помощью std::ref

async просто лучше, чем это, но только для науки это можно сделать:

void myfunc_reference(int& i);
std::thread(myfunc_reference, std::ref(output));

Я подозреваю, что реализация async должна сделать что-то вроде этого для нас, что по сути то, что вы должны сделать в pthread: как вернуть значение из потоков pthread в C?

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

В следующем примере исполняемого кода сравнивается асинхронный и этот худший метод:

main.cpp

#include <cassert>
#include <future>
#include <iostream>
#include <thread>
#include <vector>

int myfunc(int i) {
    return i + 1;
}

void myfunc_reference(int& i) {
    i = myfunc(i);
}

int main() {
    unsigned int nthreads = 4;
    std::vector<int> inputs{1, 2, 3, 4};
    std::vector<int> outputs_expect{2, 3, 4, 5};

    // future and sync. Nirvana. When you are not fighting to death with types:
    // https://stackoverflow.com/questions/10620300/can-stdasync-be-use-with-template-functions
    {
        std::vector<std::future<int>> futures(nthreads);
        std::vector<int> outputs(nthreads);
        for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
            futures[i] = std::async(
                myfunc,
                inputs[i]
            );
        }
        for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
            outputs[i] = futures[i].get();
        }
        assert(outputs_expect == outputs);
    }

    // Reference arguments.
    //
    // Annoying because requires:
    //
    // - wrapping the return function to accept references
    // - keeping an array of outputs
    // - std::ref
    {
        std::vector<std::thread> threads(nthreads);
        std::vector<int> inouts(inputs);
        for (decltype(threads)::size_type i = 0; i < nthreads; ++i) {
            threads[i] = std::thread(myfunc_reference, std::ref(inouts[i]));
        }
        for (auto& thread : threads) {
            thread.join();
        }
        assert(outputs_expect == inouts);
    }
}

GitHub вверх по течению.

Скомпилируйте и запустите с:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp -pthread
./main.out

Проверено в Ubuntu 19.04.

Ответ 5

Вот более конкретный пример.

Функция имитирует загрузку с параметром обратного вызова, чтобы показать прогресс и отменить загрузку.

namespace __HeavyWork
{
    int SimulateDownload(std::function<int(int)> dlCallBack)
    {
        for (size_t i = 0; i < 100; i++)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));

            if (dlCallBack(i) == -1)
            {
                return i;
            }
        }

        return 100;
    }
}

Мы хотим получить статус загрузки,

#include <thread>
#include <future>

    void test()
    {
        auto simulateCancellation = []()->bool {
            static bool b = true;
            if (b)
            {
                srand((unsigned int)time(NULL));
                b = false;
            }
            return (rand() % 7) == 0;
        };

        auto funDLCallback = [&](int i)->int {
            if (simulateCancellation())
            {
                return -1;
            }
            cout << "download: " << i << endl;
            return i;
        };
        auto funDownload = [&](std::promise<int> && p) {
            p.set_value(__HeavyWork::SimulateDownload(funDLCallback));
        };



        std::promise<int> p;
        auto f = p.get_future();
        std::thread t(funDownload, std::move(p));
        //dlg.doModal();
        t.join();
        cout << "return value: " << f.get() << endl;
    }

Ответ 6

Я думаю, что ссылочная переменная читается намного более интуитивно.

int x = 0;
std::thread theThread(threadFunction, std::ref(x));
theThread.join();
std::cout << x << std::endl;

Ответ 7

Все зависит от вашего определения "простой".

Использование фьючерсов, безусловно, сделает магический трюк в нескольких строках С++, но я считаю, что он спорит с захватом механизма, который был разработан для параллельной обработки для такого простого использования.

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

Использование всего механизма в качестве обходного пути для получения простого возвращаемого значения int - это то, что я называю синтаксическим программным обеспечением. Он сидит в качестве витрины для универсальности С++ 11, но он скрывает значительное потребление ресурсов под обледенением синтаксического сахара.

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

для (спорных) удобств его или эстетической привлекательности, то, что эти виды трюков достижения исключения <сильного > функционально, которые можно было бы считать достаточно полезными, чтобы компенсировать скрытые расходы?

Читая такие ответы без надлежащего многозадачного фона, новые дети на блоке могут быть искушаемы, используя регулярные механизмы неэффективности. Это будет только загрязнять будущее программное обеспечение большим количеством случаев синдрома Шлемиеля Painter.