在一个线程中调用一个std::move_only_function
对象并在另一个线程中替换它指向的函数是否安全?
我的代码:
#include <future>
#include <functional>
int main() {
std::move_only_function<void()> fn = []{};
auto future1 = std::async(std::launch::async, [&]{ fn(); });
auto future2 = std::async(std::launch::async, [&]{ fn = []{}; });
future1.get();
future2.get();
}
我不在乎调用的是旧函数还是新函数。我也知道它fn
始终包含一个有效函数。
不,这并不安全。
赋值运算符 (
std::move_only_function::operator=
) 不能保证是原子的。因此,您可以在其中进行上下文切换,使
std::move_only_function
对象处于无效状态,另一个线程将尝试调用该状态,从而导致未定义的行为。最大的问题是,如果使用小缓冲区优化(在 MSVC 上其大小为 5 个指针),如果您的 lambda 捕获任何内容并且在函数运行时被替换,那么您正在运行的 lambda 捕获将被损坏。
您不仅希望它的分配是线程安全的,您也不希望它在运行时被重新分配,您会希望在调用它之前将其移出,或者在执行它时用 RW 锁锁定它。
如果您想要线程安全,更好的选择是使用指向仅移动函数的原子(共享)指针。
std::move_only_function 本身不提供线程安全保证,如果正确处理同步和访问控制,则可以在多线程上下文中安全地使用它。