gpt4 book ai didi

c++ - 通过 std::thread 传递右值

转载 作者:搜寻专家 更新时间:2023-10-31 00:53:15 25 4
gpt4 key购买 nike

这是一个为什么这个代码不能工作的问题。我想知道如何修复命名空间 dj 中的代码,而不是演示程序中的代码。在进一步阅读之前,您可能需要运行该程序。

当我通过 std::thread 传递 rvalue std::string 时,字符串到达​​并行函数时为空。也许原始值已被 std::moved,但最终位于错误的位置。但我认为问题可能出在函数 timer 上。我认为字符串是通过引用捕获的,当右值消失时,引用无效。

// In the demo program, the string is the one element of `...args`.
template<typename F, typename... Args>
void timer(double seconds, F& f, Args&&... args) {
auto th = std::thread(
[&, seconds] { delayed(seconds, f, std::forward<Args>(args)...); });
th.detach();
}

我不知道如何为 lambda 捕获数据。符号让我害怕,但我无法解决它。我曾多次尝试使用 bindfunction 代替 lambda。没有快乐。

...

演示程序... 主线程启动一个暂停给定秒数的进程。然后新线程打印一个字符串并发出哔哔声,直到主线程将原子 bool 值设置为真。要显示它不起作用,请将全局 bool demo_the_problem 设置为 true。字符串到达​​报警函数时为空。

#include <thread>
#include <chrono>
#include <iostream>

static bool demo_the_problem = false;

namespace dj {

inline void std_sleep(long double seconds) noexcept
{
using duration_t = std::chrono::duration<long long, std::nano>;
const auto duration = duration_t(static_cast<long long> (seconds * 1e9));
std::this_thread::sleep_for(duration);
}

// Runs a command f after delaying an amount of time
template<typename F, typename... Args>
auto delayed(double seconds, F& f, Args&&... args) {
std_sleep(seconds);
return f(std::forward<Args>(args)...);
}

// Runs a function after a given delay. Returns nothing.
template<typename F, typename... Args>
void timer(double seconds, F& f, Args&&... args) {
auto th = std::thread( // XXX This appears to be where I lose ring_tone. XXX
[&, seconds] { delayed(seconds, f, std::forward<Args>(args)...); });
th.detach();
}

}

using namespace dj;

int main() {

std::atomic<bool> off_button(false);

// Test dj::timer, which invokes a void function after a given
// period of time. In this case, the function is "alarm_clock".
auto alarm_clock = [&off_button](const std::string ring_tone) {
char bel = 7;
std::cout << ring_tone << '\n';
while (!off_button) {
std_sleep(0.5);
std::cout << bel;
}
off_button = false;
};

auto ring = std::string("BRINNNNGGG!");
if (demo_the_problem)
timer(4.0, alarm_clock, std::string("BRINNNNGGG!")); // Not OK - ring arrives as empty string
else {
timer(4.0, alarm_clock, ring); // Ring tone arrives intact.
}


// Mess around for a while
for (int i = 0; i < 12; ++i) {
if (i == 7) {
off_button = true; // Hit the button to turn off the alarm
}
std::cout << "TICK ";
std_sleep(0.5);
std::cout << "tock ";
std_sleep(0.5);
}

// and wait for the button to pop back up.
while(off_button) std::this_thread::yield();
std::cout << "Yawn." << std::endl;
return 0;
}

最佳答案

template<typename F, typename... Args>
void timer(double seconds, F& f, Args&&... args) {
auto th = std::thread(
[&, seconds] { delayed(seconds, f, std::forward<Args>(args)...); });
th.detach();
}

这会导致未定义的行为,因为引用捕获的数据销毁没有相对于线程中的代码排序。

永远不要在生命周期(或其拷贝)超过当前作用域的 lambda 上使用 &。期间。

你的detach也是代码味道;没有实用的方法来确定线程是否在 main 结束之前完成,并且比 main 还长的线程具有未指定的行为。这是 C++,您有责任清理您的资源使用情况。找到解决方案。我暂时忽略它。

template<typename F, typename... Args>
void timer(double seconds, F&& f, Args&&... args) {
auto th = std::thread(
[seconds,
f=std::forward<F>(f),
tup=std::make_tuple(std::forward<Args>(args)...)
]
{
// TODO: delayed(seconds, f, std::forward<Args>(args)...);
}
);
th.detach();
}

现在我们只需要编写 //TODO 行。

` 这很简单。

template<typename F, typename... Args>
void timer(double seconds, F&& f, Args&&... args) {
auto th = std::thread(
[seconds,
f=std::forward<F>(f),
tup=std::make_tuple(std::forward<Args>(args)...)
]() mutable
{
std::apply(
[&](auto&&...args){
delayed(seconds, f, decltype(args)(args)...);
},
std::move(tup)
);
}
);
th.detach();
}

请注意,此结果所有内容都复制到线程中。如果您真的非常想传递一个左值引用,请使用 std::ref,它最适合您。

最好的解决方案是编写自己的 notstd::apply。有很多人写过这个,包括myself here .

注意我在 lambda 中使用了 [&]; lambda 不会超过当前范围(事实上它不会超过当前行)。那是您唯一应该使用 [&] 的情况。

关于c++ - 通过 std::thread 传递右值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49387752/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com