C++ 没有互斥的线程安全静态变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1052168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:34:34  来源:igfitidea点击:

Thread-safe static variables without mutexing?

c++boostinitializationthread-safetyc++03

提问by Gili

I remember reading that static variables declared inside methods is not thread-safe. (See What about the Meyer's singleton?as mentioned by Todd Gardner)

我记得读过在方法中声明的静态变量不是线程安全的。(参见Todd Gardner提到的 Meyer 单身人士怎么样?

Dog* MyClass::BadMethod()
{
  static Dog dog("Lassie");
  return &dog;
}

My library generates C++ code for end-users to compile as part of their application. The code it generates needs to initialize static variables in a thread-safe cross-platform manner. I'd like to use boost::call_onceto mutex the variable initialization but then end-users are exposed to the Boost dependency.

我的库为最终用户生成 C++ 代码以作为其应用程序的一部分进行编译。它生成的代码需要以线程安全的跨平台方式初始化静态变量。我想使用boost::call_once互斥变量初始化,但最终用户会暴露于 Boost 依赖项。

Is there a way for me to do this without forcing extra dependencies on end-users?

有没有办法让我在不强迫最终用户额外依赖的情况下做到这一点?

采纳答案by Todd Gardner

You are correct that static initialization like that isn't thread safe (hereis an article discussing what the compiler will turn it into)

你是对的,像这样的静态初始化不是线程安全的(是一篇讨论编译器将它变成什么的文章)

At the moment, there's no standard, thread safe, portable way to initialize static singletons. Double checked locking can be used, but you need potentially non-portable threading libraries (see a discussion here).

目前,没有标准的、线程安全的、可移植的方式来初始化静态单例。可以使用双重检查锁定,但您可能需要不可移植的线程库(请参阅此处的讨论)。

Here's a few options if thread safety is a must:

如果必须保证线程安全,这里有几个选项:

  1. Don't be Lazy (loaded): Initialize during static initialization. It could be a problem if another static calls this function in it's constructor, since the order of static initialization is undefined(see here).
  2. Use boost (as you said) or Loki
  3. Roll your own singleton on your supported platforms (should probably be avoided unless you are a threading expert)
  4. Lock a mutex everytime you need access. This could be very slow.
  1. 不要懒惰(加载):在静态初始化期间初始化。如果另一个静态在其构造函数中调用此函数,则可能会出现问题,因为静态初始化的顺序未定义(请参阅此处)。
  2. 使用 boost(如你所说)或 Loki
  3. 在支持的平台上滚动你自己的单例(除非你是线程专家,否则应该避免)
  4. 每次需要访问时锁定互斥锁。这可能非常缓慢。

Example for 1:

示例 1:

// in a cpp:
namespace {
    Dog dog("Lassie");
}

Dog* MyClass::BadMethod()
{
  return &dog;
}

Example for 4:

示例 4:

Dog* MyClass::BadMethod()
{
  static scoped_ptr<Dog> pdog;
  {
     Lock l(Mutex);
     if(!pdog.get())
       pdog.reset(new Dog("Lassie"));
  }
  return pdog.get();
}

回答by Steve Jessop

Not sure whether this is what you mean or not, but you can remove the boost dependency on POSIX systems by calling pthread_onceinstead. I guess you'd have to do something different on Windows, but avoiding that is exactly why boost has a thread library in the first place, and why people pay the price of depending on it.

不确定这是否是您的意思,但您可以通过调用pthread_once来消除对 POSIX 系统的 boost 依赖。我想你必须在 Windows 上做一些不同的事情,但是避免这正是 boost 拥有线程库的原因,以及为什么人们要为依赖它付出代价。

Doing anything "thread-safely" is inherently bound up with your threads implementation. You have to depend on something, even if it's only the platform-dependent memory model. It is simply not possible in pure C++03 to assume anything at all about threads, which are outside the scope of the language.

做任何“线程安全”的事情本质上都与你的线程实现有关。你必须依赖一些东西,即使它只是平台相关的内存模型。在纯 C++03 中根本不可能假设任何关于线程的事情,这超出了语言的范围。

回答by 1800 INFORMATION

One way you could do it that does not require a mutex for thread safety is to make the singleton a file static, rather than function static:

一种不需要线程安全互斥锁的方法是使单例成为静态文件,而不是静态函数:

static Dog dog("Lassie");
Dog* MyClass::BadMethod()
{
  return &dog;
}

The Doginstance will be initialised before the main thread runs. File static variables have a famous issue with the initialisation order, but as long as the Dog does not rely on any other statics defined in another translation unit, this should not be of concern.

Dog实例将在主线程运行之前进行初始化。文件静态变量在初始化顺序方面有一个著名的问题,但只要 Dog 不依赖于另一个翻译单元中定义的任何其他静态变量,就不必担心。

回答by dcw

AFAIK, the only time this has been done safely and without mutexes or prior initialisation of global instances is in Matthew Wilson's Imperfect C++, which discusses how to do this using a "spin mutex". I'm not near to my copy of it, so can't tell you any more precisely at this time.

AFAIK,唯一一次在没有互斥锁或全局实例的事先初始化的情况下安全地完成此操作是在 Matthew Wilson 的Imperfect C++ 中,它讨论了如何使用“自旋互斥锁”来做到这一点。我不知道我的副本,所以现在不能更准确地告诉你。

IIRC, there are some examples of the use of this inside the STLSoftlibraries, though I can't remember which components at this time.

IIRC,在STLSoft库中有一些使用 this 的例子,虽然我现在不记得是哪些组件。

回答by paxdiablo

The only way I know of to guarantee you won't have threading issues with non-protected resources like your "static Dog"is to make it a requirement that they're all instantiated beforeany threads are created.

我所知道的确保您不会遇到像您这样的非受保护资源的线程问题的唯一方法"static Dog"是要求创建任何线程之前将它们全部实例化。

This could be as simple as just documenting that they have to call a MyInit()function in the main thread before doing anything else. Then you construct MyInit()to instantiate and destroy one object of each type that contains one of those statics.

这可能就像记录他们必须MyInit()在执行任何其他操作之前在主线程中调用一个函数一样简单。然后您构造MyInit()以实例化和销毁包含这些静态之一的每种类型的一个对象。

The only other alternative is to put another restriction on how they can use your generated code (use Boost, Win32 threads, etc). Either of those solutions are acceptable in my opinion - it's okay to generate rules that they must follow.

唯一的另一种选择是对他们如何使用您生成的代码(使用 Boost、Win32 线程等)施加另一个限制。在我看来,这些解决方案中的任何一个都是可以接受的 - 可以生成他们必须遵循的规则。

If they don't follow the rules as set out by your documentation, then all bets are off. The rule that they must call an initialization function or be dependent on Boost is not unreasonable to me.

如果他们不遵守您的文档中规定的规则,那么所有赌注都将被取消。他们必须调用初始化函数或依赖于 Boost 的规则对我来说并非不合理。