c++: std::线程的简单返回值



使用win32线程,我有直接的GetExitCodeThread(),它给了我线程函数返回的值。我正在寻找std::thread(或boost线程)类似的东西
据我所知,这可以通过期货来实现,但具体如何实现呢?

查看c++ 11未来的视频教程。

显式地使用线程和future:

#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)。

我会说:

#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甚至传播任何从线程函数抛出的异常

使用c++ 11线程,无法获得作为线程出口的返回值,这是pthread_exit(...)

的情况。

您需要使用c++ 11 Future<>来获取返回值。Future是使用模板参数创建的,其中模板接受返回值(内置的用户定义类型).

可以在另一个线程中使用future<..>::get(..)函数获取该值。

使用future<..>的一个好处是你可以检查返回值的有效性,即如果它已经被取走,你可以避免使用future<..>::isValid(...)函数检查get()的有效性。

下面是你将如何编写代码。

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

通过std::ref

传递一个引用/指针给线程

async只是比这好,但只是为了科学,它可以做到:

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

我怀疑async的实现必须在我们的引擎盖下做一些事情,这基本上是你必须在pthread后端做的事情:如何从C中的pthread线程返回值?

当然,你必须确保变量的生命周期持续到线程返回。

下面的可运行代码示例比较了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上游。

编译并运行:

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

我认为引用变量读起来更直观。

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

下面是一个更具体的例子。

一个函数用一个回调参数模拟下载,以显示下载进度和取消下载。

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

您可以使用下面的代码创建一组带有线程和期货的线程:

#include <thread>
#include <future>
void func(promise<float> && prms) {
    prms.set_value(0.125);
}

in main:

vector<pair<thread, future<float>>> threads;
for (int i = 0; i < std::thread::hardware_concurrency(); i++) {
                promise<float> prms;
                future<float> fut = prms.get_future();
                ///Triggering threads
                thread th(func, move(prms));
                ///Pushing thread and result to vector
                threads.push_back(make_pair(move(th), move(fut)));
          
        }
        cout<< "Killing threads ... n";
        for (auto& e : threads)
        {
            auto th = move(e.first);
            auto fut = move(e.second);
            float flt = fut.get();
            //cout << flt << endl;
            th.detach();
        }

这完全取决于你对"简单"的定义。

在c++的几行代码中,使用future当然可以达到神奇的效果,但是我发现为了这样一个微不足道的用途而劫持一个为并行处理而设计的机制是有争议的。

future在多核cpu上大多是有意义的,它们允许进程启动不同步的任务,这些任务将从其他核心中吸取计算能力(撇开寻找一组足够大的不相关数据的事实,值得付出努力也不是一件小事)。

使用整个机制作为获取int返回值的变通方法,我称之为语法驱动的软件设计。它很好地展示了c++ 11的多功能性,但是在语法糖衣下隐藏了大量的资源消耗。

如果不是使用其他内核作为原始电源,创建一个线程的意义是什么,如果你不打算与它通信,直到它完成?
因为如果你这样做了,从它中检索任何状态,无论是在终止还是其他任何时候,都将是一件微不足道的事情,你甚至不会首先想到使用期货。

除了(有争议的)方便或美观之外,这些技巧在上实现了什么功能,可以被认为足够有用以抵消隐藏的成本?

在没有适当的多任务处理背景的情况下阅读这样的答案,新来的孩子可能会经常使用这种低效的机制。
这只会给未来的软件带来更多的画家Schlemiel综合症。

最新更新