std :: call_once,应何时使用?

时间:2019-04-03 08:05:10

标签: c++ c++17

std::call_once https://en.cppreference.com/w/cpp/thread/call_once

确保以线程安全的方式恰好一次调用了callable。

由于还有其他类似的方法,问题是:

何时应使用?它打算解决什么类型的问题?

请提供示例。

6 个答案:

答案 0 :(得分:8)

示例:我将它用于libcURL来从网站检索http(s)数据。在libcURL中,必须使用one-time global initialization才能使用该库。鉴于初始化不是线程安全的,但是从网站请求数据是线程安全的,所以我使用call_once,它只调用一次初始化,无论在哪个线程中以及是否同时调用

答案 1 :(得分:2)

典型用法是在可能发生争用(多线程)的情况下按需初始化全局数据的情况。

假设您有结构

struct A{ A() {/*do some stuff*/} };

,您需要在全局范围内使用它的一个实例。

如果执行以下操作,则会在main之前对其进行初始化,因此它不是按需的。

A a_global;

如果您执行以下操作,则该请求是必需的,但它不是线程安全的。

A *a_singleton = NULL;
A *getA() { 
   if (!a_singleton)
      a_singleton = new A();
   return a_singleton;
}

call_once解决了这两个问题。当然,您可以使用其他同步原语的某种组合来代替,但最终只会重新实现自己的call_once版本。

答案 2 :(得分:1)

想象一个单例实例,其中包含一些巨大的数据(由于某种原因):

class Singleton {
    public:  static Singleton& get();
    ...
    private: static std::unique_ptr<SingletonDataBase> instance;
}

我们如何确保get函数在正确调用时创建实例(,无论出于何种原因,该实例确实很大,并且不能进入静态内存空间)。我们如何实现这一目标?

  1. 使用mutex吗?我猜是很丑。
  2. 使用std::call_once吗?更好,并坚定地给出了代码的意图:

Singleton& Singleton::get() {
    static std::once_flag flag;
    std::call_once(flag, [&](){ instance.reset(new SingletonDataBase()); });
    return instance.get_interface()
}

每当您需要精确调用一次时,使用call_once很高兴。

答案 3 :(得分:0)

  

何时应使用?

您想打一次电话。简洁明了。

替代

struct CallFooOnce { 
    CallFooOnce() { 
        foo(); 
    } 
}; 
static CallFooOnce foo_once;

具有更多样板,并在上方加上另一个名称

static std::once_flag foo_once;
std::call_once(foo_once, foo);

答案 4 :(得分:0)

缓存和惰性求值。假设一个不可变类有一个存储成本低但计算成本高的属性,double foo() const;。与其按需计算或预先计算,不如添加 mutable std::once_flag m_flag; mutable double m_foo; 然后可以执行

double foo() const {
    std::call_once(m_flag, [this] { m_foo = doCalcFoo(); });
    return m_foo;
}

答案 5 :(得分:-1)

std::call_once() 可用于惰性求值,传递给它的可调用对象即使有多个线程执行,也只会执行一次。请参见下一个示例,其中方法 getInstance() 可能会被多个线程调用多次,但是该实例仅在需要时创建一次(第一次调用 getInstance())。< /p>

#include <mutex>

class Singleton {
  static Singleton *instance;
  static std::once_flag inited;

  Singleton() {...} //private

public:
  static Singleton *getInstance() {
    std::call_once( inited, []() {
        instance=new Singleton();
      });
    return instance;
  } 

};