【问题标题】:Is the std::function object itself thread safe? If no, what ways can I make it thread safe?std::function 对象本身是线程安全的吗?如果不是,我可以通过什么方法使其线程安全?
【发布时间】:2021-07-15 13:32:25
【问题描述】:

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

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

令人惊讶的是,互联网上只有少数几个网站谈论这个,而且所有(我遇到的)都谈论它指向的功能,不是对象本身

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

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

如果你阅读它们,你会发现没有一个在谈论 std::function 对象本身的安全性。

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

  • 同时读取同一个对象是可以的

  • 不同对象的同时读/写是可以的

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

那么,我怎样才能使它线程安全?一种选择是使用互斥锁,但由于以下示例,这将无法完全发挥作用:

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

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

【问题讨论】:

  • “线程可以改变它指向的函数”——那么它就需要被保护,就像其他不是本能原子的东西一样。 调用它指向的函数与被调用的代码一样是线程安全的;不多也不少。即使你在第一种情况下复制,你仍然需要保护,只要有一个潜在的作家在混合的某个地方。
  • 除非某些内容被明确记录为线程安全,否则您必须假设没有线程安全

标签: c++ multithreading stl std


【解决方案1】:

函数对象与所有其他对象一样。只是数据,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"
    }
}

【讨论】:

    【解决方案2】:

    您可以通过分配或交换来更改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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-31
      • 2021-04-27
      • 2016-08-14
      • 2010-10-15
      • 1970-01-01
      相关资源
      最近更新 更多