【发布时间】:2020-10-04 10:23:38
【问题描述】:
问题
我正在尝试将 lambda-closure 传递给 std::thread,它使用任意封闭参数调用任意封闭函数。
template< class Function, class... Args >
std::thread timed_thread(Function&& f, Args&&... args) {
// Regarding capturing perfectly-forwarded variables in lambda, see [1]
auto thread_thunk = ([&] {
std::cout << "Start thread timer" << std::endl;
// Regarding std::invoke(_decay_copy(...), ...), see (3) of [2].
// Assume no exception can be thrown from copying.
std::invoke(_decay_copy(std::forward<Function>(f)),
_decay_copy(std::forward<Args>(args)...));
}
}
int main() {
int i = 3;
std::thread t = timed_thread(&print_int_ref, std::ref(i));
t.join()
return 0;
}
/*
[1]: https://stackoverflow.com/questions/26831382/capturing-perfectly-forwarded-variable-in-lambda
[2]: https://en.cppreference.com/w/cpp/thread/thread/thread
*/
- 我使用
std::forward以便转发(正确发送)右值引用和左值引用。 - 由于
std::invoke和 lambda 创建临时数据结构,调用者必须将引用包装在std::ref中。
代码似乎可以工作,但会导致stack-use-after-scope 进行地址清理。这是我的主要困惑。
嫌疑人
我认为这可能与this error 有关,但我没有看到这种关系,因为我没有返回参考;对i 的引用应该在main 的堆栈帧期间有效,因为main 加入了它,所以它应该比线程更持久。引用通过副本 (std::reference_wrapper) 传递到 thread_thunk。
我怀疑args...不能被引用捕获,那么应该如何捕获呢?
第二个困惑:将{std::thread t = timed_thread(blah); t.join();}(强制析构函数的大括号)更改为timed_thread(blah).join(); 不会产生这样的问题,尽管在我看来它们是等价的。
小例子
#include <functional>
#include <iostream>
#include <thread>
template <class T>
std::decay_t<T> _decay_copy(T&& v) { return std::forward<T>(v); }
template< class Function, class... Args >
std::thread timed_thread(Function&& f, Args&&... args) {
// Regarding capturing perfectly-forwarded variables in lambda, see [1]
auto thread_thunk = ([&] {
std::cout << "Start thread timer" << std::endl;
// Regarding std::invoke(_decay_copy(...), ...), see (3) of [2].
// Assume no exception can be thrown from copying.
std::invoke(_decay_copy(std::forward<Function>(f)),
_decay_copy(std::forward<Args>(args)...));
std::cout << "End thread timer" << std::endl;
});
/* The single-threaded version code works perfectly */
// thread_thunk();
// return std::thread{[]{}};
/* multithreaded version appears to work
but triggers "stack-use-after-scope" with ASAN */
return std::thread{thread_thunk};
}
void print_int_ref(int& i) { std::cout << i << std::endl; }
int main() {
int i = 3;
/* This code appears to work
but triggers "stack-use-after-scope" with ASAN */
// {
// std::thread t = timed_thread(&print_int_ref, std::ref(i));
// t.join();
// }
/* This code works perfectly */
timed_thread(&print_int_ref, std::ref(i)).join();
return 0;
}
编译器命令:clang++ -pthread -std=c++17 -Wall -Wextra -fsanitize=address test.cpp && ./a.out。 Remvoe address 看看它的工作原理。
【问题讨论】:
标签: c++ multithreading c++17 move-semantics