线程安全的“Nifty Counter”(又名“Schwarz Counter”)

Pau*_*cas 5 c++ multithreading initialization c++17

代码为“俏皮计数器”(又名中,“施瓦茨计数器”)

// Stream.h
#ifndef STREAM_H
#define STREAM_H

struct Stream {
  Stream ();
  ~Stream ();
};
extern Stream& stream; // global stream object

static struct StreamInitializer {
  StreamInitializer ();
  ~StreamInitializer ();
} streamInitializer; // static initializer for every translation unit

#endif // STREAM_H
Run Code Online (Sandbox Code Playgroud)
// Stream.cpp
#include "Stream.h"

#include <new>         // placement new
#include <type_traits> // aligned_storage

static int nifty_counter; // zero initialized at load time
static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type
  stream_buf; // memory for the stream object
Stream& stream = reinterpret_cast<Stream&> (stream_buf);

Stream::Stream ()
{
  // initialize things
}
Stream::~Stream ()
{
  // clean-up
} 

StreamInitializer::StreamInitializer ()
{
  if (nifty_counter++ == 0) new (&stream) Stream (); // placement new
}
StreamInitializer::~StreamInitializer ()
{
  if (--nifty_counter == 0) (&stream)->~Stream ();
}
Run Code Online (Sandbox Code Playgroud)

似乎它是线程安全的......除了这个

在主函数开始执行之前,所有具有静态存储持续时间的非局部变量都作为程序启动的一部分进行初始化......

到现在为止还挺好。

(除非延期,见下文)。

哦哦。

动态初始化是发生在main函数的第一条语句之前(对于静态函数)还是线程的初始函数(对于线程局部变量),或者推迟到之后发生,这是实现定义的。

如果在第一个 Nifty 计数器static对象的动态初始化被推迟到main函数的第一条语句之后发生,并且函数的第一条语句main创建了一个线程并且该线程访问stream,那么如何保证流对象已经完全在第二个线程中访问之前在主线程中初始化?

该代码是否包含竞争条件?如果是这样,你会如何修复它?使用读/写锁?


编辑:使计数器原子化并不能解决问题。即使计数器是原子的,也不能保证在第二个线程上访问对象之前构造函数已在原始线程上完成。与保证线程安全的函数局部静态构造函数不同,通过放置调用的构造函数没有这样的保证new