具有原子成员的单例 class 的实现
Implementation of a singleton class with atomic members
我正在尝试实施 class 来跟踪多线程代码的性能,并且 运行 遇到了以下问题。
跟踪器应该有一个实例,因此是单例模式。
为了计算所有线程中的对象创建和函数执行,我认为使用原子成员是个好主意。但我似乎无法找出正确的实现方式。
这是我想做的最简单的代码:
#include <atomic>
#include <iostream>
class Counter
{
public:
static Counter& instance()
{
return instance_;
};
void increment()
{
counter_++;
};
private:
Counter ()
{
std::cout << "ctor counter" << std::endl;
};
~Counter ()
{
std::cout << "counter_: " << counter_ << std::endl;
std::cout << "dtor counter" << std::endl;
}
static Counter instance_;
std::atomic<int> counter_{0};
//int counter_ = 0;
};
Counter Counter::instance_ = Counter();
int main(void)
{
Counter::instance().increment();
Counter::instance().increment();
Counter::instance().increment();
return 0;
}
如果 counter_
变量是 int
,它工作正常,但不是线程安全的。
如果它是 atomic<int>
,那么编译器会告诉我:
g++ foo.cc
foo.cc:34:38: error: use of deleted function 'Counter::Counter(const Counter&)'
Counter Counter::instance_ = Counter();
^
foo.cc:4:7: note: 'Counter::Counter(const Counter&)' is implicitly deleted because the default definition would be ill-formed:
class Counter
^~~~~~~
foo.cc:4:7: error: use of deleted function 'std::atomic<int>::atomic(const std::atomic<int>&)'
In file included from foo.cc:1:0:
/usr/include/c++/7/atomic:668:7: note: declared here
atomic(const atomic&) = delete;
^~~~~~
我不确定我是否完全理解这个问题。任何 explanation/solution 将不胜感激。
干杯
您可以通过让 std::atomic<int> counter_{0};
简单地成为 static
class 成员而不是每个实例的一部分来简化。 (因为您要确保 class 只有一个实例。)
或者,如果您只使用 "singleton" 返回对静态对象的引用,只需将其所有成员设为 static
,这样您甚至不需要获取指针首先是它的那个单一实例。然后它可以只是一个美化的 namespace{}
与 public 和私有静态成员函数。我认为 "singleton" 的唯一要点是通过使用带有非常量初始化器的函数范围静态来延迟初始化它直到静态初始化之后,但你没有这样做。
实际问题出在您的 class 的复制构造函数中,您的静态初始化程序使用您编写它的方式。 它构造一个临时的Counter();
,然后将其复制到静态instance_
变量。
您可以编译为 C++17,其中保证删除该副本 (works on Godbolt with g++ -std=gnu++17
with your source untouched),或者您可以重写初始化程序
Counter Counter::instance_; // default construct
Counter Counter::instance_{}; // explicitly default construct
两者都适用于 g++ -std=gnu++11
我正在尝试实施 class 来跟踪多线程代码的性能,并且 运行 遇到了以下问题。
跟踪器应该有一个实例,因此是单例模式。 为了计算所有线程中的对象创建和函数执行,我认为使用原子成员是个好主意。但我似乎无法找出正确的实现方式。
这是我想做的最简单的代码:
#include <atomic>
#include <iostream>
class Counter
{
public:
static Counter& instance()
{
return instance_;
};
void increment()
{
counter_++;
};
private:
Counter ()
{
std::cout << "ctor counter" << std::endl;
};
~Counter ()
{
std::cout << "counter_: " << counter_ << std::endl;
std::cout << "dtor counter" << std::endl;
}
static Counter instance_;
std::atomic<int> counter_{0};
//int counter_ = 0;
};
Counter Counter::instance_ = Counter();
int main(void)
{
Counter::instance().increment();
Counter::instance().increment();
Counter::instance().increment();
return 0;
}
如果 counter_
变量是 int
,它工作正常,但不是线程安全的。
如果它是 atomic<int>
,那么编译器会告诉我:
g++ foo.cc
foo.cc:34:38: error: use of deleted function 'Counter::Counter(const Counter&)'
Counter Counter::instance_ = Counter();
^
foo.cc:4:7: note: 'Counter::Counter(const Counter&)' is implicitly deleted because the default definition would be ill-formed:
class Counter
^~~~~~~
foo.cc:4:7: error: use of deleted function 'std::atomic<int>::atomic(const std::atomic<int>&)'
In file included from foo.cc:1:0:
/usr/include/c++/7/atomic:668:7: note: declared here
atomic(const atomic&) = delete;
^~~~~~
我不确定我是否完全理解这个问题。任何 explanation/solution 将不胜感激。
干杯
您可以通过让 std::atomic<int> counter_{0};
简单地成为 static
class 成员而不是每个实例的一部分来简化。 (因为您要确保 class 只有一个实例。)
或者,如果您只使用 "singleton" 返回对静态对象的引用,只需将其所有成员设为 static
,这样您甚至不需要获取指针首先是它的那个单一实例。然后它可以只是一个美化的 namespace{}
与 public 和私有静态成员函数。我认为 "singleton" 的唯一要点是通过使用带有非常量初始化器的函数范围静态来延迟初始化它直到静态初始化之后,但你没有这样做。
实际问题出在您的 class 的复制构造函数中,您的静态初始化程序使用您编写它的方式。 它构造一个临时的Counter();
,然后将其复制到静态instance_
变量。
您可以编译为 C++17,其中保证删除该副本 (works on Godbolt with g++ -std=gnu++17
with your source untouched),或者您可以重写初始化程序
Counter Counter::instance_; // default construct
Counter Counter::instance_{}; // explicitly default construct
两者都适用于 g++ -std=gnu++11