简体   繁体   English

"C++:来自 std::thread 的简单返回值?"

[英]C++: Simple return value from std::thread?

With win32 threads I have the straight forward GetExitCodeThread() that gives me the value which the thread function returned.使用 win32 线程,我有直接的GetExitCodeThread() ,它给了我线程函数返回的值。 I'm looking for something similar for std::thread (or boost threads)我正在为std::thread (或提升线程)寻找类似的东西
As I understand this can be done with futures but how exactly?据我了解,这可以通过期货来完成,但究竟如何?

See this video tutorial on C++11 futures.请参阅有关 C++11 期货的视频教程

Explicitly with threads and futures:明确地使用线程和期货:

#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();

Or with std::async (higher-level wrapper for threads and futures):或者使用std::async (线程和期货的高级包装器):

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

I can't comment whether it works on all platforms (it seems to work on Linux, but doesn't build for me on Mac OSX with GCC 4.6.1).我无法评论它是否适用于所有平台(它似乎适用于 Linux,但不适用于带有 GCC 4.6.1 的 Mac OSX)。

I'd say:我会说:

#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;
}

Note that async even propagates any exceptions thrown from the thread function请注意,异步甚至会传播从线程函数抛出的任何异常

I think a reference variable reads much more intuitively.我认为参考变量读起来更直观。

std::mutex m;

void threadFunction(int& val) {
    std::lock_guard<std::mutex> guard(m);
    val = 5;
}

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

Using C++11 threads, one can't get the return value as thread exit which used to be the case with pthread_exit(...)使用 C++11 线程,无法在线程退出时获得返回值,而pthread_exit(...)

You need to use C++11 Future<> to get the return value.您需要使用 C++11 Future<>来获取返回值。 Future is created using templated argument where the template takes the return value (built in of User Defined types).. Future 是使用模板化参数创建的,其中模板采用返回值(内置于用户定义类型)。

You can fetch the value in another thread using future<..>::get(..) function.您可以使用future<..>::get(..)函数在另一个线程中获取值。

one benefit of using future<..> is that you can check the validity of return value ie if it's already taken, you avoid calling get() accidentally by checking the validity using future<..>::isValid(...) function.使用future<..>一个好处是你可以检查返回值的有效性,即如果它已经被采用,你get()通过使用future<..>::isValid(...)检查有效性来避免意外调用get() future<..>::isValid(...)功能。

Here is how you'll write the code.以下是您将如何编写代码。

#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;
}

it should also be noted that we can get the future run on the same thread by using launch::deferred option as还应该注意的是,我们可以通过使用launch::deferred选项让未来在同一线程上运行

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

Pass a reference / pointer to the thread with std::ref使用std::ref将引用/指针传递给线程

async is just better than this, but just for science, it can be done: async只是比这更好,但只是为了科学,它可以做到:

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

I suspect that the implementation of async must be doing something along those lines under the hood for us, which is essentially what you have to do in the pthread backend: How to return a value from pthread threads in C?我怀疑async的实现必须在幕后为我们做一些事情,这本质上是你必须在pthread后端做的事情: 如何从 C 中的 pthread 线程返回值?

You have to ensure of course that the variable lifetime lasts until the thread returns.当然,您必须确保变量生命周期持续到线程返回。

The following runnable code example compares async and this worse method:下面的可运行代码示例比较了 async 和这种更糟糕的方法:

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 upstream . GitHub 上游.

Compile and run with:编译并运行:

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

Tested in Ubuntu 19.04.在 Ubuntu 19.04 中测试。

Here is a more specific example.这是一个更具体的例子。

A function simulates download with a callback parameter to show progress and to cancel the download.函数使用回调参数模拟下载以显示进度并取消下载。

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;
    }
}

We want to get the download status,我们想获取下载状态,

#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;
    }

You can create a set of threads using the code below with threads and futures:您可以使用以下带有线程和期货的代码创建一组线程:

#include <thread>
#include <future>

void func(promise<float> && prms) {

    prms.set_value(0.125);

}

It all depends on your definition of "simple".这一切都取决于您对“简单”的定义。

Using futures will certainly do the magic trick in a few lines of C++, but I find it debatable to hijack a mechanism that was designed for parallel processing for such a trivial use.使用期货肯定会在几行 C++ 中实现魔术,但我发现劫持一个专为并行处理而设计的机制来实现这种微不足道的用途是值得商榷的。

futures mostly make sense on multicore CPUS, where they allow a process to launch unsynchronized tasks that will leech computing power from the other cores (leaving aside the fact that finding a set of uncorelated data big enough to be worth the effort is not such a trivial matter either). futures 主要在多核 CPU 上有意义,它们允许进程启动不同步的任务,这些任务将从其他内核中汲取计算能力(撇开这样一个事实,即找到一组大到值得付出努力的不相关数据并不是一件容易的事也没关系)。

Using the whole mechanism as a workaround to retrieve a mere int return value is what I call syntax-driven software design.使用整个机制作为一种变通方法来检索纯粹的 int 返回值,这就是我所说的语法驱动的软件设计。 It sits pretty as a showcase for C++11 versatility, but it does hide considerable resource consumption under an icing of syntactic sugar.它很好地展示了 C++11 的多功能性,但它确实在语法糖霜下隐藏了大量的资源消耗。

If not for using other cores as raw power source, what is the point in creating a thread if you don't mean to communicate with it until it's done?如果不是使用其他内核作为原始电源,如果您不想在线程完成之前与其进行通信,那么创建线程有什么意义?
Because if you do, retrieving whatever status from it, be it at termination or anytime else, will be a trivial matter and you will not even think of using futures in the first place.因为如果你这样做,从中检索任何状态,无论是在终止时还是在其他任何时候,都将是一件小事,你甚至不会首先考虑使用期货。

Except for the (arguable) convenience of it or the aesthetic appeal, what do these kinds of tricks achieve functionally that could be considered useful enough to offset the hidden costs?除了它的(有争议的)便利性或审美吸引力之外,这些类型的技巧在功能上实现了什么,可以被认为足够有用以抵消隐藏成本?

Reading such answers with no proper multitasking background, the new kids on the block might be tempted by using these kind of inefficient mechanisms on a regular basis.在没有适当的多任务处理背景的情况下阅读此类答案,街区的新孩子可能会因定期使用这些低效机制而受到诱惑。
That would only pollute future software with more cases of Schlemiel the Painter 's syndrom.那只会用更多的施莱米尔画家综合症来污染未来的软件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM