全局静态初始化线程 [英] Global static initialization threading

查看:168
本文介绍了全局静态初始化线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个集合,我用互斥保护。在初始化之后,它只能被读取,所以我不需要一个互斥体。



集合被初始化并在全局静态初始化器中填充。我知道全局静态初始化在单个翻译单元内是有保证的。是否有任何保证全局静态初始化将是单线程的?






我有一个由Schwarz计数器保护的静态集合并由其他静态对象的构造函数填充。容器与互斥体相关联。假设在 main 启动后,集合是只读的,我想摆脱互斥,如果我能保证静态构造函数在单个线程中被调用。 p>

我的理解是,静态初始化顺序通常在单个翻译单元中明确定义,但在翻译单元之间未指定。标准是否允许静态对象由不同的运行时提供的线程来初始化/构造?






Schwarz计数器: p>

头文件:

  struct Init 
{
Init();
〜Init();
};
namespace
{
Init _init;
}
extern std :: map< int,std :: unique_ptr< ...>> & protected;

源文件:

  namespace 
{
int init_count;
std :: aligned_storage< sizeof(std :: map< int,std :: unique_ptr< ...>>),alignof(std :: map< int,std :: unique_ptr& ;>)> protected_storage;
}
std :: map< int,std :: uniqe_ptr< ...>> & protected = * reinterpret_cast< std :: map< int,std :: unique_ptr< ...>> *>(& protected_storage);
Init :: Init()
{
if(!init_counter ++)
{
new(& protected_storage)std :: map< int,std :: unique_ptr< ; ...>>>();
}
}
Init ::〜Init()
{
if(! - init_counter)
{
protected。〜std :: map< int,std :: unique_ptr< ...>>();
}
}






集合人口:

  struct helper 
{
helper(...)
{
protected.insert(std :: make_pair(...));
}
};

一个宏展开后会创建助手的静态实例。

解决方案


有没有保证全局静态初始化将是单线程的?


你的意思是动态初始化。



p>如果程序启动线程(30.3),则相对于在不同的翻译
单元中定义的变量的初始化,变量的后续初始化
不会被排序。否则,变量的初始化相对于在不同的翻译单元中定义的变量的初始化
不确定地排序。如果一个程序启动一个线程,一个变量的后续无序的
初始化对于每个其它的动态初始化是不定的。否则,
对于每个其他动态
初始化,变量的无序初始化不确定地排序


如果你在你的程序中启动一个线程,那么来自两个不同TU的两个不同的全局变量理论上可以使它们的构造函数同时从两个不同的线程运行。



处理这些问题是将你的静态存储持续时间变量作为局部静态变量包装在下面的单例模式中:

  const夯; f()
{
static T t(a,b,c);
return t;
}

最新的标准保证了 t 是线程安全的,所以你不需要一个互斥体(至少没有明确指定,编译器会为你生成保护)。



作为一个附加的好处,对象在第一次调用 f 时被懒惰构造,所以你不需要担心初始化顺序。如果多个这样的单例在它们的构造函数中相互调用(假定依赖性当然是非循环的),它们将以工作顺序被初始化。非局部变量不是这种情况。


I have a collection that I am protecting with a mutex. After initialization it is only ever read, so I won't need a mutex there.

The collection is initialized and populated in global static initializers. I know that global static initialization is guaranteed within a single translation unit. Is there any guarantee that global static initialization will be single threaded?


I have a static collection that protected by a Schwarz counter and is populated by constructors of other static objects. The container is associated with a mutex. Given that the collection is read-only after main starts, I would like to get rid of the mutex if I can guarantee that static constructors are called in a single thread.

My understanding is that static initialization order is generally well defined within a single translation unit, but unspecified between translation units. Does the standard allow for the static objects to be initialized/constructed by different runtime provided threads?


Schwarz counter:

Header file:

struct Init
{
   Init();
   ~Init();
};
namespace
{
   Init _init;
}
extern std::map<int, std::unique_ptr<...>> &protected;

Source file:

namespace
{
   int init_count;
   std::aligned_storage<sizeof(std::map<int, std::unique_ptr<...>>), alignof(std::map<int, std::unique_ptr<...>>>)> protected_storage;
}
std::map<int, std::uniqe_ptr<...>> &protected = *reinterpret_cast<std::map<int, std::unique_ptr<...>> *>(&protected_storage);
Init::Init()
{
   if (!init_counter++)
   {
      new(&protected_storage) std::map<int, std::unique_ptr<...>>();
   }
}
Init::~Init()
{
   if (!--init_counter)
   {
      protected.~std::map<int, std::unique_ptr<...>>();
   }
}


Collection population:

struct helper
{
   helper(...)
   {
      protected.insert(std::make_pair(...));
   }
};

A macro is expanded that creates static instances of helper.

解决方案

Is there any guarantee that global static initialization will be single threaded?

You mean dynamic initialization. No, single threaded initialization is explicitly not guaranteed.

From 3.6.2:

If a program starts a thread (30.3), the subsequent initialization of a variable is unsequenced with respect to the initialization of a variable defined in a different translation unit. Otherwise, the initialization of a variable is indeterminately sequenced with respect to the initialization of a variable defined in a different translation unit. If a program starts a thread, the subsequent unordered initialization of a variable is unsequenced with respect to every other dynamic initialization. Otherwise, the unordered initialization of a variable is indeterminately sequenced with respect to every other dynamic initialization

So if you start a thread in your program then two different global variables from two different TUs could theoretically have their constructors running at the same time from two different threads.

The best way to deal with these issues is to wrap your static storage duration variables as local static variables in the following "singleton pattern":

const T& f()
{
    static T t(a,b,c);
    return t;
}

The latest standard guarantees that the construction of t is thread-safe, so you will not need a mutex at all (at least not one explicitly specified, the compiler will generate the guard for you).

As an added benefit, the object is constructed "lazily" on the first call to f, so you don't need to worry about initialization order. If multiple such singletons call each other in their constructors (provided the dependencies are acyclic of course), they will be initialized in a working order. This is not the case for non-local variables.

这篇关于全局静态初始化线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆