从std::thread中获取返回代码?

10
4个回答

19

不,那不是 std::thread 的用途。

相反,使用 async 来获得一个 future

#include <future>

int myfun(double, char, bool);

auto f = std::async(myfun, arg1, arg2, arg3);  // f is a std::future<int>

// ...

int res = f.get();
你可以使用 fwait_for 成员函数(超时时间为零)来查看结果是否准备好。

4
@Klaim:我对packaged_task不是很了解,也不知道它的习惯用法。请您将其发布为答案; 这对每个人都有好处。 - Kerrek SB
“valid”并不告诉您结果何时准备好,它告诉您未来有一个关联状态,并且因此最终会获得一个值。 - Anthony Williams
1
是的,我会使用wait_for和零超时来检查是否准备就绪。 - Anthony Williams
3
哦,我没意识到你就是我刚买书的那个人!太令人兴奋了。 - Kerrek SB
1
@YagamyLight:它可能会阻塞。当然,你不会创建一个async的future并立即调用get。这个想法是,你有其他事情要做,只有在绝对需要结果时才会阻塞。 - Kerrek SB
显示剩余7条评论

16

正如其他人所建议的,可以使用<future>中的设施来实现此目的。然而,我反对以下答案:

不,您无法使用std::thread完成这项任务。

以下是使用std::thread实现您想要的一种方式。这绝不是唯一的方法:

#include <thread>
#include <iostream>

int func(int x)
{
    return x+1;
}

int main()
{
    int i;
    std::thread t([&] {i = func(2);});
    t.join();
    std::cout << i << '\n';
}

这将会输出可移植的内容:
3

1
是的,但你并不是真正从thread对象中获取它;你是因为创建了一个使用引用来检索变量的函数对象。任何人都可以这样做;问题是如何捕获thread对象的返回值。 - Nicol Bolas
1
这个问题有两个句子。我回答了第二个句子。 - Howard Hinnant
@HowardHinnant:当然,你可以让线程操作任何旧的共享状态(这可能是你应该做的方式)。但你仍然没有“获取std::thread的返回值”...相反,你有一种类似于“packaged_task”的穷人版,我想。但这肯定是一个非常有用的观察。(顺便说一句,第二个句子不是一个问题;-)。) - Kerrek SB

14
Kerrek SB 的回答是正确的,但我建议添加另一个例子(他建议将其作为答案,所以这里是)。最近我发现,在 VC11 中,std::async 不会释放线程的所有资源,直到应用程序结束,这可能会导致 内存泄漏误报(如果您正在使用 Visual Leak Detector 等监视它们)。在此,我指的是在大多数基本应用程序中,不值得查看本答案的其余部分,但是如果像我一样需要检查内存泄漏并且不能容忍假阳性,例如在 main 函数结束时未释放静态数据。 如果是这种情况,则这可能有所帮助。 std::async默认情况下不能保证在单独的线程中运行,只有当您使用std::launch::async作为第一个参数时才会这样。否则,实现会决定要做什么,这就是为什么VC11实现将使用新的Microsoft Concurrency Runtime任务管理器将提供的函数作为推入任务池的任务来管理,这意味着线程以透明的方式进行维护和管理。有一些方法可以显式地终止任务管理器,但这太具体于平台,使得async在以下情况下不是很好的选择:1)确保启动一个线程并且2)稍后获得结果并且3)确保在线程释放结果时完全释放线程


实现这一点的替代方法是使用std::packaged_taskstd::threadstd::future结合使用。它的实现方式几乎类似于使用std::async,只是稍微冗长一些(这意味着您可以将其泛化为自定义模板函数,如果您想要的话)。

#include <packaged_task>
#include <thread>

int myfun(double, char, bool);

std::packaged_task<int(double, char, bool)> task(myfun, arg1, arg2, arg3); 

auto f = task.get_future();  // f is a std::future<int> 

首先,我们创建一个任务,基本上是一个包含函数和与future相关联的std :: promise的对象。std :: packaged_task主要像std :: function的增强版本:
现在我们需要显式地执行线程:
std::thread thread(std::move(task));

thread.detach();

这个操作是必要的,因为std::packaged_task不能被复制。如果你只想使用future进行同步,那么分离线程是必要的 - 否则你需要显式地加入线程。如果不这样做,在线程的析构函数被调用时,它将会调用std::terminate()
// ...

int res = f.get(); // Synchronization and retrieval.

正如我刚刚阅读的那样,如果函数查询结果,即实际上只是等待线程返回,那么std::async可能不会异步运行,在这种情况下,对于单独的线程确实没有理由。 - Hi-Angel

3
这里有一个使用packaged_task的示例:
#include <future>
#include <iostream>

void task_waiter(std::future<int>&& f) {
    std::future<int> ft = std::move(f);
    int result = ft.get();
    std::cout << result << '\n';
}

int the_task() {
    return 17;
}

int main() {
    std::packaged_task<int()> task(the_task);
    std::thread thr(task_waiter, task.get_future());
    task();
    thr.join();
    return 0;
}

主线程如何访问结果? - Kerrek SB
@KerrekSB - 反过来做:从packaged_task对象获取一个future对象,然后将packaged_task对象传递给运行任务的新线程。 - Pete Becker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接