【问题标题】:Do objects captured by a lambda exist for as long as the lambda?lambda 捕获的对象是否存在与 lambda 一样长的时间?
【发布时间】:2023-01-10 12:10:20
【问题描述】:
我一直认为 lambda 只是函数指针,但我从未想过认真使用捕获语句......
如果我创建一个通过副本捕获的 lambda,然后将该 lambda 移动到一个完全不同的线程并且不尝试保存 lambda 中使用的原始对象,它会为我保留那些副本吗?
std::thread createThread() {
std::string str("Success");
auto func = [=](){
printf("%s", str.c_str());
};
str = "Failure";
return std::thread(func);
}
int main() {
std::thread thread = createThread();
thread.join();
// assuming the thread doesn't execute anything until here...
// would it print "Success", "Failure", or deference a dangling pointer?
return 0;
}
【问题讨论】:
标签:
c++
multithreading
lambda
【解决方案1】:
保证打印Success。按副本捕获完全按照它说的去做。它就在那里制作对象的副本并将该副本存储为闭包对象的一部分。从捕获创建的闭包对象的成员与闭包对象本身一样长。
lambda 不是函数指针。 Lambda 是可以具有内部状态的通用函数对象,这是函数指针所没有的。事实上,只有无捕获的 lambda 可以转换为函数指针,因此有时可能表现得像一个。
lambda 表达式产生一个基本上看起来像这样的闭包类型:
struct /*unnamed1*/ {
/*unnamed1*/(const /*unnamed1*/&) = default;
/*unnamed1*/(/*unnamed1*/&&) = default;
/*unnamed1*/& operator=(const /*unnamed1*/&) = delete;
void operator() const {
printf("%s", /*unnamed2*/.c_str());
};
std::string /*unnamed2*/;
};
而func是这个类型的对象,/*unnamed2*/被初始化为str的当前值。
【解决方案2】:
你有3种情况
- 您可以通过设计保证变量比线程的寿命更长,因为您在变量超出范围之前与线程的末尾同步。
- 您知道您的线程可能比您的线程的范围/生命周期更长,但您不再需要从任何其他线程访问变量。
- 您不能说哪个线程寿命最长,您有多个线程访问您的数据并且您想延长变量的寿命
情况 1. 通过引用捕获
情况 2. 按值捕获(或者你甚至使用移动)变量
在案例 3 中,使数据共享,std::shared_ptr 并按值捕获它
情况 3 会将数据的生命周期延长到最长存活线程的生命周期。