我的单例可以被多次调用

My singleton can be called multiple times

我实现了一个基于c++11的单例。 但是在某些情况下可以多次调用构造函数。

class会被编译成静态库和 被其他 so lib 使用(不止一个 so lib)。 并且系统是多线程系统(运行在Android HAL级别)

/// .h 文件:

class Logger
{
public:

    /// Return the singleton instance of Logger
    static Logger& GetInstance() {
        static Logger s_loggerSingleton;
        return s_loggerSingleton;
    }

private:

    /// Constructor
    Logger();
    /// Destructor
    ~Logger();
}

/// .cpp 文件

Logger::Logger()
{
   ALOGE("OfflineLogger create");
}

Logger::~Logger()
{

}

应该创建一次 eg:

03-21 01:52:20.785   728  4522 E         : OfflineLogger create

但是我可以看到它已经创建了不止一次

03-21 01:52:20.785   728  4522 E         : OfflineLogger create
03-21 01:52:20.863   728  2274 E         : OfflineLogger create
03-21 01:52:20.977   728  2273 E         : OfflineLogger create
03-21 01:52:26.370   728  4522 E         : OfflineLogger create

问题:

  1. 我的单例设计有什么问题吗?是线程安全的问题吗?

  2. 似乎我的单例在一个 so 范围内工作正常,但每个 所以包含我的单例的库将创建自己的单例,这样我的 单例不再是“成为单例”。问题是由每个 动态链接到新的 so 并且 "static variable" 变成 "local static"? 可能吗?如果是,如何解决?

  1. Anything wrong with my singleton design? Is it a thread-safe issue?

没有。函数局部 static 变量的初始化由标准保证是线程安全的。

  1. Seems like my singleton works fine in one so scope, but each so lib which include my singleton will create its own singleton, so that my singleton is no longer “be a singleton”. Is the problem caused from each dynamic linking to new so and the "staic veriable" become "local static"? Is it possible? If so, how to fix

这是正确的结论。

与其创建包含单例实现的静态库,不如将其设为动态库。

应将静态变量移至 .cpp 文件。

简单的方法是在 .h 中只保留 getInstance() 的声明,并将实现移至 .cpp 文件。

这里有一个 idea:Rather 而不是使用单例,这在您的环境中既是一个难题,又是已知的测试和维护问题,只需将您的代码设计为仅创建一个有问题的对象。

单例很难,尤其是共享库。

您的每个共享库都有一个非共享库的独立副本。不加小心,每个人都会有一份单例。

为了拥有非平凡的单例,我必须做的是

  1. 创建一个极低级别的库来帮助单例——称之为 LibSingleton

  2. 创建一个知道单例类型的单例模板。它使用魔法静态向 LibSingleton 发送请求,其中包含大小、typeid(T).name() 键以及类型擦除的构造和销毁代码。 LibSingleton return 是一个引用计数 RAII 对象。

  3. LibSingleton 使用共享互斥体来 return 匹配 name/size 或构造它的先前构造的对象。如果它构造对象,它存储销毁代码。

  4. 当 LibSingleton 数据的最后一个引用计数句柄消失时,LibSingleton 运行销毁代码并清理其无序映射中的内存。

这允许几乎在任何地方使用非常简单的单例。

template<class T>
class singleton {
public:
  static T& Instance() {
    static auto smart_ptr = LibSingleton::RequestInstance(
      typeid(T).name(),
      sizeof(T),
      [](void* ptr){ return ::new( ptr ) T{}; },
      [](void* ptr){ static_cast<T*>(ptr)->~T(); }
    );
    if (!smart_ptr)
      exit(-1); // or throw something
    return *static_cast<T*>(smart_ptr.get());
  }
protected:
  singleton() = default;
  ~singleton() = default;
private:
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;
};

使用看起来像:

struct Logger : LibSingleton::singleton<Logger> {
  friend class LibSingleton::singleton<Logger>;
  void do_log( char const* sting ) {}
private:
  Logger() { /* ... */ }
};