这种创建静态实例线程的方式安全吗?

发布于 2024-10-08 09:18:01 字数 399 浏览 0 评论 0原文

我有以下示例 C++ 代码:

class Factory
{
public:
    static Factory& createInstance()
    {
        static Factory fac;
        return fac;
    }

private:
    Factory() 
    {
        //Does something non-trivial
    }
};

假设两个线程同时调用 createInstance。那么最终的对象会被正确创建吗?如果当第一个线程位于 Factory 的构造函数中时,第二个线程进入 createInstance 调用,会发生什么情况?

I have the following sample C++ code:

class Factory
{
public:
    static Factory& createInstance()
    {
        static Factory fac;
        return fac;
    }

private:
    Factory() 
    {
        //Does something non-trivial
    }
};

Let's assume that createInstance is called by two threads at the same time. So will the resulting object be created properly? What happens if the second thread enters the createInstance call when the first thread is in the constructor of Factory?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

掌心的温暖 2024-10-15 09:18:02

实例化(第一次调用)本身是线程安全

但是,一般来说,后续访问将不会。例如,假设实例化后,一个线程调用一个可变的 Factory 方法,另一个线程调用 Factory 中的某个访问器方法,那么您就会遇到麻烦。

例如,如果您的工厂记录了创建的实例数量,那么如果该变量周围没有某种互斥体,您就会遇到麻烦。

但是,如果 Factory 确实是一个没有状态(没有成员变量)的类,那么就可以了。

The instantiation (first call) itself is threadsafe.

However, subsequent access will not be, in general. For instance, suppose after instantiation, one thread calls a mutable Factory method and another calls some accessor method in Factory, then you will be in trouble.

For example, if your factory keeps a count of the number of instances created, you will be in trouble without some kind of mutex around that variable.

However, if Factory is truly a class with no state (no member variables), then you will be okay.

ζ澈沫 2024-10-15 09:18:01

C++11 及更高版本:本地静态创建是线程安全的。

标准保证:

  • 创建是同步的。
  • 如果创建抛出异常,则下次执行流经过变量定义点时,将再次尝试创建。

它通常通过双重检查来实现:

  • 首先检查线程本地标志,如果设置,则访问该变量。
  • 如果尚未设置,则采用更昂贵的同步路径,如果随后创建变量,则设置线程本地标志。

C++03 和 C++98:标准不知道线程。

就标准而言,不存在线程,因此标准中没有关于跨线程同步的规定。

然而,有些编译器以扩展的形式或通过提供更强的保证实现了比标准要求更多的内容,因此请检查您感兴趣的编译器。如果它们是高质量的编译器,那么很可能他们会保证这一点。

最后,它可能没有必要是线程安全的。如果您在创建任何线程之前调用此方法,那么您可以确保在真正的多线程发挥作用之前正确初始化该方法,并且您将巧妙地回避该问题。

C++11 and above: local static creation is thread-safe.

The standard guarantees that:

  • The creation is synchronized.
  • Should the creation throws an exception, the next time the flow of execution passes the variable definition point, creation will be attempted again.

It is generally implemented with double-checking:

  • first a thread-local flag is checked, and if set, then the variable is accessed.
  • if not yet set, then a more expensive synchronized path is taken, and if the variable is created afterward, the thread-local flag is set.

C++03 and C++98: the standard knows no thread.

There are no threads as far as the Standard is concerned, and therefore there is no provision in the Standard regarding synchronization across threads.

However some compilers implement more than the standard mandates, either in the form of extensions or by giving stronger guarantees, so check out for the compilers you're interested in. If they are good quality ones, chances are that they will guarantee it.

Finally, it might not be necessary for it to be thread-safe. If you call this method before creating any thread, then you ensures that it will be correctly initialized before the real multi-threading comes into play, and you'll neatly side-step the issue.

森林散布 2024-10-15 09:18:01

看看此页面,我会说这不是线程安全的,因为在最终分配变量之前可以多次调用构造函数。可能需要 InterlockedCompareExchange(),您可以在其中创建变量的本地副本,然后通过互锁函数将指针原子分配给静态字段(如果静态变量为 null)。

Looking at this page, I'd say that this is not thread-safe, because the constructor could get called multiple times before the variable is finally assigned. An InterlockedCompareExchange() might be needed, where you create a local copy of the variable, then atomically assign the pointer to a static field via the interlocked function, if the static variable is null.

梦回旧景 2024-10-15 09:18:01

当然是线程安全的!除非你是个十足的疯子,从静态对象的构造函数中生成线程,否则在调用 main() 之前你不会有任何线程,并且 createInstance 方法只是返回对已构造对象的引用,这是不可能的失败。 ISO C++ 保证在调用 main() 后第一次使用之前构造对象:不能保证在调用 main 之前构造对象,但必须在第一次使用之前构造对象,因此所有系统都会在调用 main() 之前执行初始化main() 被调用。当然,ISO C++ 没有定义存在线程或动态加载时的行为,但主机级计算机的所有编译器都提供此支持,并将尽可能保留为单线程静态链接代码指定的语义。

Of course it's thread safe! Unless you are a complete lunatic and spawn threads from constructors of static objects, you won't have any threads until after main() is called, and the createInstance method is just returning a reference to an already constructed object, there's no way this can fail. ISO C++ guarantees that the object will be constructed before the first use after main() is called: there's no assurance that will be before main is called, but is has to be before the first use, and so all systems will perform the initialisation before main() is called. Of course ISO C++ doesn't define behaviour in the presence of threads or dynamic loading, but all compilers for host level machines provide this support and will try to preserve the semantics specified for singly threaded statically linked code where possible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文