C++实现一个线程安全且高效单例类

本文介绍了C++中实现线程安全单例模式的几种方法,包括经典的懒汉式和饿汉式实现,并讨论了它们之间的差异及适用场景。

转载自:http://www.cnblogs.com/qiaoconglovelife/p/5851163.html

C++实现一个线程安全且高效单例类

1 单例模式

  我们都很清楚一个简单的单例模式该怎样去实现:构造函数声明为private或protect防止被外部函数实例化,内部保存一个private static的类指针保存唯一的实例,实例的动作由一个public的类方法代劳,该方法也返回单例类唯一的实例。

  上代码:  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class singleton
{
protected:
    singleton(){}
private:
    static singleton* p;
public:
    static singleton* instance();
};
singleton* singleton::p = NULL;
singleton* singleton::instance()
{
    if (p == NULL)
        p = new singleton();
    return p;
}

  这是一个很棒的实现,简单易懂。但这是一个完美的实现吗?不!该方法是线程不安全的,考虑两个线程同时首次调用instance方法且同时检测到p是NULL值,则两个线程会同时构造一个实例给p,这是严重的错误!同时,这也不是单例的唯一实现!

2 懒汉与饿汉

  单例大约有两种实现方法:懒汉与饿汉。

    • 懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;
    • 饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。

  特点与选择:

    • 由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
    • 在访问量较小时,采用懒汉实现。这是以时间换空间。
3 线程安全的懒汉实现

  线程不安全,怎么办呢?最直观的方法:加锁。

  • 方法1:加锁的经典懒汉实现:
复制代码
class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};

pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
    if (p == NULL)
    {
        pthread_mutex_lock(&mutex);
        if (p == NULL)
            p = new singleton();
        pthread_mutex_unlock(&mutex);
    }
    return p;
}
复制代码
  • 方法2:内部静态变量的懒汉实现

  此方法也很容易实现,在instance函数里定义一个静态的实例,也可以保证拥有唯一实例,在返回时只需要返回其指针就可以了。推荐这种实现方法,真得非常简单。    

 

复制代码
class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
    int a;
};

pthread_mutex_t singleton::mutex;
singleton* singleton::initance()
{
    pthread_mutex_lock(&mutex);
    static singleton obj;
    pthread_mutex_unlock(&mutex);
    return &obj;
}
复制代码

 

4 饿汉实现

  为什么我不讲“线程安全的饿汉实现”?因为饿汉实现本来就是线程安全的,不用加锁。为啥?自己想!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class singleton
{
protected:
    singleton()
    {}
private:
    static singleton* p;
public:
    static singleton* initance();
};
singleton* singleton::p = new singleton;
singleton* singleton::initance()
{
    return p;
}
### AOSP 中实现线程安全的 Binder 单例 在 AOSP 源码中,Binder 机制的单例实现需要考虑线程安全性。以下是几种常见的实现方式及其详细说明。 #### 1. 使用 `ThreadLocal` 实现线程安全的 Binder 单例 `IPCThreadState` 的实现使用了类似 `ThreadLocal` 的机制来保证每个线程拥有独立的实例[^1]。这种方式通过 `pthread_getspecific()` 管理每个参与 Binder 通信线程的实例,确保线程间的安全性。 ```cpp // IPCThreadState.cpp static pthread_key_t gTLSKey; sp<IPCThreadState> IPCThreadState::self() { sp<IPCThreadState>& instance = *reinterpret_cast<sp<IPCThreadState>*>(pthread_getspecific(gTLSKey)); if (instance == nullptr) { instance = new IPCThreadState(); pthread_setspecific(gTLSKey, &instance); } return instance; } ``` 此方法利用了线程本地存储(TLS),避免了多线程环境下的竞争问题。 #### 2. 在 Zygote 初始化过程中创建单例 Zygote 进程初始化时会调用 `ProcessState::self()` 创建一个全局的 `ProcessState` 单例,并启动 Binder 线程池[^2]。这种方式确保了在整个进程中只有一个 `ProcessState` 实例。 ```cpp // ZygoteInit.cpp virtual void onZygoteInit() { sp<ProcessState> proc = ProcessState::self(); proc->startThreadPool(); // 启动新的 Binder 线程池 } ``` 通过 `ProcessState::self()` 方法,系统确保了单例的唯一性和线程安全性。 #### 3. 使用静态内部类实现 Binder 单例 静态内部类是一种优雅线程安全的单例实现方式。以下是一个示例: ```cpp class BinderSingleton { private: static BinderSingleton* sInstance; BinderSingleton() {} public: static BinderSingleton* getInstance() { static class InstanceHolder { public: InstanceHolder() : mInstance(new BinderSingleton()) {} BinderSingleton* mInstance; } holder; return holder.mInstance; } }; ``` 此方法利用了 C++ 的静态局部变量初始化特性,确保了线程安全和延迟加载。 #### 4. 使用枚举实现 Binder 单例 枚举类型的单例实现天然支持线程安全,因为 JVM 在类加载时会确保枚举实例的唯一性。 ```java public enum BinderSingleton { INSTANCE; public IBinder getBinder() { return new Binder(); } } ``` 此方法简单高效,适用于 Java 层的 Binder 单例实现。 #### 5. 在 MediaServer 中的 Binder 单例实现 MediaServer 的实现展示了如何结合 `ProcessState` 和 `IPCThreadState` 来创建线程安全的 Binder 单例[^4]。 ```cpp int main(int argc __unused, char **argv __unused) { sp<ProcessState> proc(ProcessState::self()); sp<IServiceManager> sm(defaultServiceManager()); MediaPlayerService::instantiate(); ::android::hardware::configureRpcThreadpool(16, false); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); ::android::hardware::joinRpcThreadpool(); } ``` 在此实现中,`ProcessState::self()` 和 `IPCThreadState::self()` 分别确保了进程级和线程级的单例安全性。 --- ### 注意事项 - 在实现 Binder 单例时,需确保实例的生命周期与进程一致,避免频繁创建和销毁。 - 对于跨进程通信,需特别注意 Binder 对象的引用计数管理,防止内存泄漏。 - 如果单例对象需要处理大量并发请求,可以结合线程池技术优化性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值