std::function 对象本身是线程安全的吗?如果不是,我可以通过什么方式使其线程安全?

Is the std::function object itself thread safe? If no, what ways can I make it thread safe?

我有一个许多线程访问的 std::function 对象。线程可以改变它指向的函数(通过它的拷贝构造函数)并且可以调用它指向的函数。

所以,我的问题是,std::function 对象 本身 线程在哪些方面是安全的?我说的不是它指向to/calls的函数,我说的是std::function对象本身

令人惊讶的是,互联网上只有少数站点在谈论这个,而且所有这些(我遇到的)都在谈论它指向、[=57 的功能=]不是对象本身.

这些是我阅读过的主要网站:

Does std::function lock a mutex when calling an internal mutable lambda?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4348.html

如果你读过它们,你会发现其中 none 谈到了 std::function 对象本身的安全性。

根据 this 页面上的第二个答案,STL 库在这些情况下是线程安全的:

基于此,当我设置 std::function 对象指向的函数时,对于此用例,std::function 对象不是线程安全的。但我问的是以防万一,因为根据 this 问题的答案,它有某种内部互斥体。

那么,有什么方法可以让它成为线程安全的呢?一种选择是使用互斥体,但由于下面演示的示例,这不会完全起作用:

假设我有一个 std::function 对象 funcstd::mutex m 来保护函数。我锁定 m 并调用函数(以保护其他线程在调用它指向的函数时设置 func),并在函数退出时解锁 m。可以说这个函数调用将花费很长时间。这样,当一个线程想要设置 func 时,它会尝试锁定互斥锁,但会阻塞直到后一个函数调用退出。在这种情况下我能做什么?

我想过使用一个指向 std::function 对象的指针。这样,我将使用互斥锁来保护这个 指针 并且当我进行函数调用时,我锁定互斥锁只是为了从指针中检索函数对象。

函数对象与所有其他对象一样。只是数据,std::function.

中没有内置互斥锁

如果你想用互斥锁保护函数,你自己需要创建一个互斥锁并在与函数交互时使用(将它指向其他东西或调用它)。

#include <functional>
#include <mutex>
#include <iostream>

int main() {
    std::mutex functionMutex;


    std::function<void()> f = [&functionMutex] {
        std::cout << "hello\n" << std::endl;
    };

    { // Imagine a new thread
        // Procect when calling
        auto s = std::scoped_lock{functionMutex};
        f();
    }

    { // You could wrap it in another function
       auto call = [&f, &functionMutex] {
         auto s = std::scoped_lock{functionMutex};
         f();
       };

       // Using this pointer would be safe
       call();
    }
    
    { // Imagine another thread
        auto s = std::scoped_lock{functionMutex};
        f = [] {}; // Changing the function "pointer"
    }
}

您可以通过赋值或交换来更改std::function。很明显,您需要保护对它的访问,就像对您分配给的任何其他非原子对象一样。您通常不会通过复制构造函数更改对象,因为复制构造函数会创建一个新对象。

为了保护对 std::function 对象的访问,您不需要指针。

std::function<void()> fun, funCopy, otherFun;
std::mutex mutex;

// thread 1
{ 
   auto s = std::scoped_lock{mutex};
   funCopy = fun;
}
funCopy();

// thread 2
{ 
   auto s = std::scoped_lock{mutex};
   fun = otherFun;
}