如何检索指向在单独线程上运行的函数的移动指针?

How to retrieve a moved pointer to a function that runs on a separate thread?

当函数在单独的线程上运行时,如何检索已移入该函数的 std::unique_ptr 指针?

考虑以下示例:

using IntPtr = std::unique_ptr<int>;

IntPtr&& update(IntPtr &&fp_)
{
    *fp_ = 23;
    return std::move(fp_);
}

int main()
{
    IntPtr foo_ptr = std::make_unique<int>(0);
    foo_ptr = update(std::move(foo_ptr));
    std::cout<< "data: " << *foo_ptr <<std::endl;
}

这很好。 foo_ptr 移入 update() 上的 fp_,然后检索(移回)到 foo_ptr

如果 update() 是 运行 在单独的线程上,我如何检索 foo_ptr???

using IntPtr = std::unique_ptr<int>;

IntPtr&& update(IntPtr &&fp_)
{
    *fp_ = 23;
    return std::move(fp_);
}

int main()
{
    IntPtr foo_ptr = std::make_unique<int>(0);
    std::thread foo_thread(update,std::move(foo_ptr));
    foo_thread.join();
    std::cout<< "data: " << *foo_ptr <<std::endl; // SEGFAULT because foo_ptr is a nullptr.
}

在线代码示例:https://rextester.com/MQDVU97718

你不知道。

您将内存的所有权交给了另一个线程。该线程现在拥有该内存并将在它终止时将其删除。 join后不存在了,你也没有把指针移回主线程。

如果你想让一个线程为另一个线程填充一些数据,那么要么你需要使用共享所有权,要么生产线程根本不应该拥有内存(即:你传递一个原始指针)。

或者只使用 packaged_task<int(int)>,你根本不需要(直接)分配内存:

int update(int value)
{
    return 23;
}

int main()
{
    int value = 0;
    std::packaged_task<int(int)> task(update);
    auto future_int = task.get_future();

    std::thread foo_thread(std::move(task), value);
    foo_thread.join();
    std::cout<< "data: " << future_int.get() <<std::endl;
}

最简单的方法是使用一个包装函数将结果写回到 foo_ptr:

std::thread foo_thread([&foo_ptr]() {
    foo_ptr = update(std::move(foo_ptr));
});

foo_thread.join();
std::cout<< "data: " << *foo_ptr <<std::endl;

请注意,尽管在 std::thread 构造函数调用和 join() 调用完成之间,主线程不得以任何方式访问 foo_ptr。这样做将是一场数据竞赛。在构造函数之前和 join 之后,这很好,因为这两个调用都用作同步点,但两者之间的任何访问都将是不同步的,并且会与 lambda 内部对 foo_ptr 的写入竞争。

如果这听起来太危险,请考虑一种方法,线程将结果放入 std::promise,然后主线程从相应的 std::future:

中检索对象
std::promise<IntPtr> p;
std::future<IntPtr> fut = p.get_future();
std::thread foo_thread([ptr = std::move(foo_ptr), p = std::move(p)]() mutable {
    p.set_value(update(std::move(ptr)));
});

foo_ptr = std::move(fut.get());