XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog
 

C#

public sealed class Singleton  
{  
    private static Singleton _instance;  
    private static readonly object LockObject = new object();  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态方法,用于获取实例  
    public static Singleton Instance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                lock (LockObject)  
                {  
                    if (_instance == null)  
                    {  
                        Singleton temp = new Singleton(); //不能简单 _instance = new Singleton(); cpu乱序会先给内存到_instance,然后调用构造函数
                        Volatile.Write(ref _instance, temp); //Interlocked.CompareExchange(ref _instance, temp,null);
                    }  
                }  
            }  
  
            return _instance;  
        }  
    }  
  
    // 其他方法...  
}
public sealed class Singleton
{
    private static Singleton s_value = null;
  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      if(s_value != null ) return s_value;
      Singleton temp = new Singleton();//可能会生成多个实例,但下面语句可以保证只有一个会用到;
      Interlocked.CompareExchange(ref s_value, temp, null); //保证 多线程下 只有一个 temp 被存活下来,其它的会被gc;
    }
  }
}

 

public sealed class Singleton  
{  
    private static volatile Singleton _instance;  //性能损耗,获取与释放语义
    private static readonly object LockObject = new object();  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态方法,用于获取实例  
    public static Singleton Instance  
    {  
        get  
        {  
            if (_instance == null)  
            {  
                lock (LockObject)  
                {  
                    if (_instance == null)  
                    {  
                        _instance = new Singleton();  //clr!JIT_CheckedWriteBarrier 最后把实例后的地址给_instance;
                    }  
                }  
            }  
  
            return _instance;  
        }  
    }  
  
    // 其他方法...  
}
public sealed class Singleton  
{  
    private static readonly Lazy<Singleton> LazyInstance = new Lazy<Singleton>(true);  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态属性,用于获取实例  
    public static Singleton Instance => LazyInstance.Value;  
  
    // 其他方法...  
}

 类型构造器方式,clr 保证类型构造器是线程安全

public sealed class Singleton  
{  
    private static Singleton _instance = new Singleton();  
  
    // 私有构造函数,防止外部实例化  
    private Singleton()  
    {  
        // 初始化代码  
    }  
  
    // 公共静态方法,用于获取实例  
    public static Singleton Instance  
    {  
        get  
        {              
            return _instance;  
        }  
    }  
}
object lockObject = new object();

lock (lockObject)
{
    // 访问或修改共享资源的代码
}


===================== 语法糖 

object lockObject = new object();

Monitor.Enter(lockObject); // 获取锁
try
{
    // 临界区代码:访问或修改共享资源
    // 如果这里发生异常,我们仍然需要确保锁被释放
}
finally
{
    Monitor.Exit(lockObject); // 释放锁
}

"监视器(Monitor)与可靠性" -异步异常的影响

复制代码
System.Threading.Monitor.Enter(sync);
  mov         eax,dword ptr [ebp-3Ch] 
  mov         ecx,dword ptr [eax+00000174h] 
  call        726C216D 
  nop              
try
{
  nop              
Response.Write("abc");
  ...


using System;
 2 namespace TempWebApplication1
 3 {
 4     public partial class Test : System.Web.UI.Page
 5     {
 6         private object sync = new object();
 7 
 8         protected void Page_Load(object sender, EventArgs e)
 9         {
10             //lock生成的代码与下面是相同的
11             System.Threading.Monitor.Enter(sync);
12             doSomething(); //理论上这里不应该有代码,但是在调试模试下,JIT会生成NOP汇编指令,这样这里会可能会被ThreadAbortException中断(几率非常小,但是你要知道这个原理),而finally则不会执行.
13             try
14             {
15                 Response.Write("abc");
16             }
17             finally
18             {
19                 System.Threading.Monitor.Exit(sync);
20             }
21         }
22     }
23 } 
    14:             lock(sync)
00007FFDB80613B4 48 8B 55 60          mov         rdx,qword ptr [rbp+60h]  
00007FFDB80613B8 48 8B 52 08          mov         rdx,qword ptr [rdx+8]  
00007FFDB80613BC 48 89 55 30          mov         qword ptr [rbp+30h],rdx  
00007FFDB80613C0 33 D2                xor         edx,edx  
00007FFDB80613C2 89 55 28             mov         dword ptr [rbp+28h],edx  
00007FFDB80613C5 48 8D 55 28          lea         rdx,[rbp+28h]  
00007FFDB80613C9 48 8B 4D 30          mov         rcx,qword ptr [rbp+30h]  
00007FFDB80613CD E8 DE C8 51 5C       call        00007FFE1457DCB0  
00007FFDB80613D2 90                   nop  
    15:             {
00007FFDB80613D3 90                   nop  
    16:                 Console.WriteLine("abc");
00007FFDB80613D4 48 B9 90 36 D3 5C 5B 02 00 00 mov         rcx,25B5CD33690h  
00007FFDB80613DE 48 8B 09             mov         rcx,qword ptr [rcx]  
00007FFDB80613E1 E8 BA 02 4F 5C       call        00007FFE145516A0  
00007FFDB80613E6 90                   nop  
    17:             }
00007FFDB80613E7 90                   nop  
00007FFDB80613E8 90                   nop  
00007FFDB80613E9 EB 00                jmp         00007FFDB80613EB  
00007FFDB80613EB 48 8B CC             mov         rcx,rsp  
00007FFDB80613EE E8 39 00 00 00       call        00007FFDB806142C  
00007FFDB80613F3 90                   nop  
    18: 
    19:             System.Threading.Monitor.Enter(sync);
00007FFDB80613F4 48 8B 4D 60          mov         rcx,qword ptr [rbp+60h]  
00007FFDB80613F8 48 8B 49 08          mov         rcx,qword ptr [rcx+8]  
00007FFDB80613FC E8 FF 12 56 5F       call        00007FFE175C2700  
00007FFDB8061401 90                   nop  
    20:             try
    21:             {
00007FFDB8061402 90                   nop  
    22:                 Console.WriteLine("abc");
00007FFDB8061403 48 B9 90 36 D3 5C 5B 02 00 00 mov         rcx,25B5CD33690h  
00007FFDB806140D 48 8B 09             mov         rcx,qword ptr [rcx]  
00007FFDB8061410 E8 8B 02 4F 5C       call        00007FFE145516A0  
00007FFDB8061415 90                   nop  
    23:             }
00007FFDB8061416 90                   nop  
00007FFDB8061417 90                   nop  
00007FFDB8061418 EB 00                jmp         00007FFDB806141A  
00007FFDB806141A 48 8B CC             mov         rcx,rsp  
00007FFDB806141D E8 37 00 00 00       call        00007FFDB8061459  
00007FFDB8061422 90                   nop  
    28:         }

Release

System.Threading.Monitor.Enter(sync);
00007FFDB80913B6 48 8B 4D 10          mov         rcx,qword ptr [rbp+10h]  
00007FFDB80913BA 48 8B 49 08          mov         rcx,qword ptr [rcx+8]  
00007FFDB80913BE E8 3D 13 53 5F       call        00007FFE175C2700  
00007FFDB80913C3 90                   nop  
    20:             try
    21:             {
    22:                 Console.WriteLine("abc");

 


 

Creating a Singleton Instance

Some classes of the Foundation and AppKit frameworks create singleton objects. In a strict implementation, a singleton is the sole allowable instance of a class in the current process. But you can also have a more flexible singleton implementation in which a factory method always returns the same instance, but you can allocate and initialize additional instances.The NSFileManager class fits this latter pattern, whereas theUIApplication fits the former. When you ask for an instance of UIApplication, it passes you a reference to the sole instance, allocating and initializing it if it doesn’t yet exist.

A singleton object acts as a kind of control center, directing or coordinating the services of the class. Your class should generate a singleton instance rather than multiple instances when there is conceptually only one instance (as with, for example, NSWorkspace). You use singleton instances rather than factory methods or functions when it is conceivable that there might be multiple instances someday.

To create a singleton as the sole allowable instance of a class in the current process, you need to have an implementation similar to Listing 2-15. This code does the following:

  • It declares a static instance of your singleton object and initializes it to nil.

  • In your class factory method for the class (named something like “sharedInstance” or “sharedManager”), it generates an instance of the class but only if the static instance is nil.

  • It overrides the allocWithZone: method to ensure that another instance is not allocated if someone tries to allocate and initialize an instance of your class directly instead of using the class factory method. Instead, it just returns the shared object.

  • It implements the base protocol methods copyWithZone:releaseretainretainCount, and autorelease to do the appropriate things to ensure singleton status. (The last four of these methods apply to memory-managed code, not to garbage-collected code.)

Listing 2-15  Strict implementation of a singleton

 1 static MyGizmoClass *sharedGizmoManager = nil;
 2  
 3 + (MyGizmoClass*)sharedManager
 4 {
 5     if (sharedGizmoManager == nil) {
 6         sharedGizmoManager = [[super allocWithZone:NULL] init];
 7     }
 8     return sharedGizmoManager;
 9 }
10  
11 + (id)allocWithZone:(NSZone *)zone
12 {
13     return [[self sharedManager] retain];
14 }
15  
16 - (id)copyWithZone:(NSZone *)zone
17 {
18     return self;
19 }
20  
21 - (id)retain
22 {
23     return self;
24 }
25  
26 - (NSUInteger)retainCount
27 {
28     return NSUIntegerMax;  //denotes an object that cannot be released
29 }
30  
31 - (void)release
32 {
33     //do nothing
34 }
35  
36 - (id)autorelease
37 {
38     return self;
39 }

If you want a singleton instance (created and controlled by the class factory method) but also have the ability to create other instances as needed through allocation and initialization, do not overrideallocWithZone: and the other methods following it as shown in Listing 2-15.


 

【原】ios下比较完美的单例模式,已验证

 

网上关于ios单例模式实现的帖子已经很多了,有很多版本,里面有对的也有不对的。我在使用过程中很难找到一个比较完美的方法,索性自己写一个吧,经过项目验证是比较合理的一个版本。

复制代码
static PRAutoLoginView *s_sharedInstance = nil;
+ (PRAutoLoginView *)shareInstance
{
    @synchronized(self)
    {
        if (s_sharedInstance == nil) {
            s_sharedInstance = [[[self class] hideAlloc] init];
        }
    }
    return s_sharedInstance;
}

#pragma mark --
#pragma mark singleton apis
+ (id)hideAlloc
{
    return [super alloc];
}

+ (id)alloc//彻底屏蔽掉alloc函数
{
    NSAssert(1 == 0, @"[PRAutoLoginView]please use +shareInstance instead of alloc!");
    return nil;
}

+ (id)new
{
    return [self alloc];
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
    @synchronized(self)
    {
        if (s_sharedInstance == nil) {
            s_sharedInstance = [super allocWithZone:zone];
            return s_sharedInstance;
        }
    }
    return nil;
}

- (id)copyWithZone:(NSZone *)zone
{
    NSAssert(1 == 0, @"[PRAutoLoginView]copy is not permitted!");
    [self retain];
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    return [self copyWithZone:zone];
}
复制代码

这里要注意的一点时,allocWithZone时默认调用的,即使你没有显式地调用alloc或者allocWithZone,因此需要重载


作者:Xi Yang

链接:https://www.zhihu.com/question/38101493/answer/75255810
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

来,骚年,给你个实例,我刚被坑过的:

template <typename T> class Singleton
{
public:
    static T& getInstance()
    {
        if (!instance)
            instance = new T();
        return *instance;
    }

private:
    static T* instance = nullptr;
};

这样就已经挂了。都不用考虑多线程的问题,还轮不到那个大坑发威。

如果你在main函数之前调用getInstance(),比如,一个全局对象的初始化函数里,那么由于main之前的初始化执行顺序不定,有可能实际上这样执行:

  • 调用getInstance(),instance被设为一个实例的地址;
  • instance静态变量被初始化为nullptr;
  • 后续的getInstance调用,又产生了一个实例。

于是,你不但 拥有两个不同的T实例,还八成泄漏了前一个。

这里还没考虑全局对象析构顺序不定造成的问题。

=================

再补充一条:编译器实现不一致。

  • VS2013没有noexcept。throw()并不能完全代替noexcept。
  • VS2013没有alignas,而微软内置的alignment声明机制不支持用alignof的结果作为一个参数,你只能直接给常量。
  • MMX指令集似乎已经嗝屁了,但64位GCC仍然在提供MMX的intrinsics。我已经弄不清楚这是应该还是不应该了。
  • Clang缺很多intrinsics,比如fxsave。
编辑于 2022-09-13 14:29・IP 属地北京
 
不是归人
template <typename T> class Singleton
{
public:
static T& getInstance()
{
static T instance;
return instance;
}
};
2015-12-04
知乎用户ihF64A
梅耶法,使用C++ 11才能确保ok
2017-07-25
费庄轩
看过Effective C++的就是不一样
2015-12-07
farta
各种C++守则建议说过多少遍,全局对象的初始化不要互相依赖,就是记不住。。。
2016-01-18
 
IQTester
不要使用全局静态变量,除非是整数等简单类型,就是因为静态变量初始化顺序不确定。
2015-12-05
Xi Yang
作者
简单类型好像也不定。。。
2015-12-05
IQTester
的确是,忽略了。我其实一般都是用上面人们提到的只在函数内部使用静态变量,其实就是弄个函数取代直接使用变量。
2015-12-05
梅启铭(vczh)
 
我关注的人
我自己写的singleton,除了只在你需要的时候构造以外,都会提供给你一个删除函数,你自己需要在main函数结束的时候显式调用。全局变量不会留下任何构造函数和析构函数。如果你没有析构,那就会在结束的时候被判定为内存泄露。简单粗暴。

c++11如何实现单例模式?

 
网上百度到的方法都不能通过编译,后来度娘告诉我说类静态成员变量必须在类外初始化。。。好蛋疼
还请大大从简单到复杂的多线程、析构函数等等介绍一下c++11的单件模式
被浏览
193,271
 
 
 

单例version1

单例是一个很好理解的概念,即保证类在全局只有一个实例,并提供一个该实例的全局访问点。

代码实现起来思路也很简单,我们先来写个最简单的版本

class Singleton {
public:
    static Singleton * GetInstance() {
        return instance;
    }
private:
    Singleton(){}//构造
    Singleton(const Singleton &clone){} //拷⻉构造
    Singleton& operator=(const Singleton&) {}
    static Singleton * instance;
};
Singleton* Singleton::instance = new Singleton();//静态成员需要初始化

我们怎么才能实现全局只有一个实例呢?那就是让用户无法自己定义一个Singleton对象。

将构造函数,拷贝构造,拷贝赋值运算符都设为私有成员,这样外界便无法通过构造函数创建新的实例。

之后便是给外界提供一个接口,方便外界获取全局唯一的一个实例。

我们可以用静态变量实现,设置一个singleton指针instance方便未来存储唯一singleton实例。

这里还有一个知识点,无论是静态成员还是静态方法,其存储位置都和singleton类割裂开的。

虽然静态变量声明在类内部,但是存储空间不一样,静态成员会在编译阶段存在静态区,如果有初始化,会放在data段,如果无初始化,会放在bss段。

像a,b这种普通成员,会根据定义类的位置,存储在栈空间或者堆上。

既然静态成员的存储位置和普通成员分开,自然普通成员方法无法“看到”类的静态成员,我们提供给外界的访问api也必须用静态成员方法,也就是上述的GetInstance()。

最后,c++还有个比较容易遗忘的规则,静态成员的初始化必须放在类外。(也就是代码最后一行)

饿汉/懒汉模式

单例的实现有饿汉,懒汉之分。

version1的代码实现的是饿汉模式,即在单例定义的时候(main函数之前)就行实例化,这样的好处是线程安全,在多线程的环境下无论哪个线程通过GetInstance()获得的都是同一个实例(同一个地址)

坏处是浪费了额外的存储空间,若我们导入一个库,其中定义了饿汉模型的单例类,那么即便我们后面不用这个单例,其依旧会实例化,占用空间。

那么我们当然希望这个单例类在整个程序中第一次用到的时候才实例化,不用不实例化,这种模式就是懒汉模式,我们将version1的代码浅修改一下。

class Singleton {
public:
    static Singleton * GetInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
private:
    Singleton(){}//构造
    Singleton(const Singleton &clone){} //拷⻉构造
    Singleton& operator=(const Singleton&) {}
    static Singleton * instance;
}
Singleton* Singleton::instance = nullptr;//静态成员需要初始化

我们完成了一个最简单的懒汉模式。instance指针初始化为nullptr

当我们第一次调用GetInstance()方法,此时发现instance指针是空指针,就会new一个Singleton对象并返回。

单例version2

懒汉模式的单例有个极大的问题。它是线程不安全的,假如多个线程同时调用GetInstance()发现instance指针是空指针,于是都开始new一个Singleton,这就会造成单例的崩坏,而且容易导致内存泄漏

同时new并不是一个原子操作,哪怕在高层语言,都非原子,new操作包括了三个步骤:

  • void* ptr = malloc(sizeof(Singleton)); //-分配空间
  • instance = (Singleton*)ptr; //-赋予指针步长
  • Singleton() //-执行构造函数

所以一个线程在任何一个过程都有可能被其它线程打断。会造成各种问题。

下面代码利用了双重检测+互斥锁+atomic变量+ c++11内存屏障实现了线程安全懒汉单例。

如果你对c++11的atomic和内存屏障有所疑惑,可以看我写的这篇前导文章,希望有所帮助。

玉米:多线程(一):C++11 atomic和内存序

#include <mutex>
#include <atomic>
class singleton{
private:
    static std::atomic<singleton*> instance_;//-静态实例指针
    static std::mutex mutex_;//-互斥锁
    static void Destroy(){
        singleton * temp = instance_.load(std::memory_order_relaxed);
        if(temp!=nullptr){
            delete temp;
        }
    }
    singleton(){}
public:
    singleton(const singleton &)=delete;
    singleton & operator = () = delete;
    singleton(singleton &&) = delete;
    singleton & operator = (singleton && ) = delete;
    static singleton * getInstance(){
        //-宽松序获取当前instance(无视顺序重排,即便该语句被重排到fence之后也没关系,因为最多会被重排到if语句之前)
        singleton * temp = instance_.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);//-设置acquire内存屏障,意味着以下读写不会被重排到acquire之前
        if(temp == nullptr){
            //-等待拿到互斥锁
            std::lock_guard <std::mutex> lock(mutex);
            //-需要再次判断其它线程是否已经修改了instance
            temp = instance_.load(std::memory_order_relaxed);
            if(temp == nullptr){
                //-new一个新的instance
                temp = new singleton;
                //-解除内存屏障
                std::atomic_thread_fence(std::memory_order_release);//-当前线程读写不会被重排到release之后
                //-将新的值写入到instance_里
                instance_.store(temp,std::memory_order_relaxed);
                //-注册析构函数
                atexit(Destroy);
            }
        }
        return temp;
    }
};
//-静态成员需要在类外初始化
singleton::instance_;
singleton::mutex_;

我们来逐个分析这个代码用到的机制。

  • atomic类的instance:

将instance定义成c++11的atomic类型变量,并使用load和store接口进行instance的读和写,就能保证读和写都是原子操作,即在多线程环境下读写是可靠的,不会在过程中被其它线程给打断。

  • 互斥锁:

当我们要new第一个实例并赋值给instance的时候,我们自然希望这整个过程是只有唯一一个线程在进行的,同时整个过程并不方便做成一个原子操作,所以在这里使用互斥锁。当我们获取到instance为nullptr的时候加锁,当我们完成实例化后解锁。

  • 双重检测:

双重检测为了弥补互斥锁的缺陷:也就是某个线程如果拿不到锁,就会阻塞。直到其它线程释放了锁,会重新恢复执行。

想象一个场景,线程t1第一个拿到锁,并给instance实例化,而其它线程t2,t3拿不到锁,陷入阻塞,当t1实例化完,t2拿到锁,此时t2会从获取锁代码处继续往下执行,继续给instance实例化,然而此时instance已经被t1实例化了,多次实例化自然有问题,所以在获取锁以后,需要再次判断instance是否为nullptr,这就是所谓的双重检测。

  • 内存屏障:为了保证双重检测的正确执行,我们必须避免双重检测代码在cpu处被重排,在前导文章中我讲了内存序来控制cpu重排,memory_order_acquire保证了该屏障后的读写代码不会被重排到acquire屏障之前,memory_order_release屏障保证了该屏障前的读写代码不会被重排到release屏障之后。也就意味着在acquire和release之前的代码“结冰”了,会非常严格按顺序执行。

单例 version3

上面的代码虽然实现了线程安全懒汉单例,但是用的机制太多也很麻烦。那么有没有更简单的方法呢?

答案是还真有,我们可以利用c++11的静态局部变量来实现线程安全懒汉单例,

代码如下:

class singleton{
private:
    singleton();//-将构造函数设为私有,这样用户无法自己创建对象
public:
    //-将拷贝构造,移动构造,拷贝运算符,移动运算符删除
    singleton(const singleton&) = delete;
    singleton& operator=(const singleton&) = delete;
    singleton(singleton &&) = delete;
    singleton & operator = (singleton && ) = delete;
    //-public析构,当进程结束后,内存销毁静态局部变量时会调用析构
    ~singleton();

    //-只适合c++11以上标准
    static singleton& getInstance(){
        //-静态局部变量实现懒汉模式,只有第一次调用getInstance的时候才会初始化instance,且全局只会初始化一次
        static singleton instance;
        return instance;
    }
};

首先,静态局部变量通过静态方法获得,且只有当该静态方法第一次执行的时候才会初始化,且后续再次调用该方法也不会再次初始化,这是静态局部变量的特性。

而在c++11以上标准中,静态局部变量初始化是线程安全的。

当其中一个线程初始化instance的时候,会阻塞其它线程的初始化行为。

发布于 2022-07-11 16:02・IP 属地陕西
 
 

更多回答

 
 
class Singleton {
public:
	static Singleton& getInstance(){
		static Singleton instance;
		return instance;
	}
private:
	Singleton ()= default;
	~Singleton ()= default;
	Singleton (const Singleton &)= delete;
	Singleton & operator=(const Singleton &)= delete;
};

c++11标准做法.

评论千万条,友善第一条
 
 
36 条评论
 
默认
最新
chaya
 

没错 c++11 局部静态变量已经是线性安全了

2017-06-14
你法我笑
请教一下,如果是局部静态指针对象呢?也是线性安全吗?
2022-07-24
唯心所现

缺一句:private: Singleton(){}

2018-02-08
杰子

还有拷贝赋值、移动构造、移动赋值通通=delete

2020-08-13
诸葛不亮
还有拷贝构造=delete
2020-07-22
bug拌饭
你这个是高赞,但是并不完整。建议补全。
private:
Singleton ()= default;
~Singleton ()= default;
Singleton (const Singleton &)= delete;
Singleton & operator=(const Singleton &)= delete;
2022-01-29
桃毛爸爸

《Best of All》在跨DLL的时候,不同DLL内各有一份实例耶。严格意义上上,这个不算单例啊。

2020-12-02
C加加辅导老师
一批有业务上下游关系的单例类型如何保证单例对象创建的顺序?
2019-03-09
Alives
 
作者
弄一个init统一创建。
2019-06-13
Esicer
 

编译都通不过

2018-05-13
Alives
 
作者

什么编译器

2018-05-13
bluepill
误导人啊
2022-08-20
franklinyu
04-03
 
 
无法实现延迟初始化
2022-11-26
GHome
 
是延迟的
04-02
洛江月
这个是放在头文件里面吗?
头文件里面的static 函数会不会被隐式内联,实际上每个每个包含了这个头文件的地方都有一份实现?
2019-12-03
洛江月
我又仔细查了一下,内联不会导致 静态局部变量 有多份
这样写是安全的
2019-12-03
 
但是如果是在动态库里就会有两份了,所以最好还是不要在头文件里写
2022-01-29
好似南柯一梦
头像
2019-04-09
Alives
 
作者
头像咋了
2019-06-13
sjunter
 

Singleton s = Singleton::getInstance() 会执行拷贝初始化,这样不就产生两个实例了吗?

2018-03-29
会飞天的喵

虽然时间有点久了,但还是回一下:Singleton &s = Singleton::getInstance() 可以获取引用;析构函数就正常写要做什么就好了(比如清理资源,关闭文件等)

2022-07-22
sjunter

意思是不是把s声明成引用类型就可以了?还有类的析构函数又该如何定义呢?

2018-03-30
炼金术士

函数前面的static是不是不一定需要?这个static只是用于内部链接吗

2020-12-14
Alives
 
作者
我这个是成员函数。
2020-12-14
炼金术士

如果是类的成员函数的话才要前面加static吧?

全局函数加不加static不是对外可不可见的区别吗?

void test() {
static int a = 0;
a++;
cout << a << endl;
}

int main() {
test();
test();
test();
}

这段代码可以正常使用

2020-12-14
 
 
很遗憾,vs2013不安全,坑死了
2020-10-16
Alives
 
作者

vs2015才支持,

2020-10-16
小明
 
析构时可能存在问题,无法决定析构顺序。。比如,LogSingleton(日志),ScreenSingletion(屏幕),如果ScreenSingletion析构异常,需要通过LogSingleton写LOG,但此时LogSingleton可能已经析构,这样程序就访问未构造的内存,导致未定义异常。
2022-01-29
萧涵
 
单例不保证顺序,顺序靠程序员自己保证,例如统一的Init()函数和release()函数。
2022-07-11
 
 
评论千万条,友善第一条
 
 
 
 
 

如果你只是需要全局访问,把一堆函数跟变量直接放进某个新 namespace 中就行。连 class 都不需要定义。

namespace 就是C++语言级天生的单例。你有没有想过单例根本不需要是 class?

之所以有单例模式这种东西,本质上是因为Java没有全局变量。

都写C++了,就不必坚持用class来制造单例了。

除非你需要使用的是某个系统级,无法重复获取的东西。


补充:如果需要控制某个变量的初始化时机,怎么办?答案是把它作为这个namespace的私有变量,自行控制其初始化进程。然后增加相关函数用于主动触发相关初始化。


#if !defined(AFX_WARECACHETOOLS_H__6DEC4E4B_219C_4DB9_904D_CA50F9F691CE__INCLUDED_)
#define AFX_WARECACHETOOLS_H__6DEC4E4B_219C_4DB9_904D_CA50F9F691CE__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// WareCacheTools.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CWareCacheTools window

#define USE_MUTEX 1 // 设置为1以使用CMutex,设置为0以使用CRITICAL_SECTION 

class AFX_EXT_CLASS CWareCacheTools //: public CWnd
{
// Construction
private:
    CWareCacheTools();
    CWareCacheTools(const CWareCacheTools &clone){} //拷贝构造
    CWareCacheTools& operator=(const CWareCacheTools& other) 
    { 
        if (this == &other) 
        {  
            return *this;  
        } 
        // 在这里添加必要的赋值逻辑  
        // 例如:拷贝其他成员变量的值到当前对象
        return *this; 
    }
    virtual ~CWareCacheTools();

    static void Lock();
    static void UnLock();
    static void InitCriticalSection();  
    static void RemoveCriticalSection();

    static CWareCacheTools* instance_;  
#if USE_MUTEX 
    static CMutex _mutex;
#else
    static CRITICAL_SECTION cs_;
    static volatile bool csInitialized_; // 标记临界区是否已初始化
#endif

// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CWareCacheTools)
    //}}AFX_VIRTUAL

// Implementation
public:
    static CWareCacheTools* Instance();
    // 静态方法用于在DLL卸载时清理资源 ,DLL_PROCESS_DETACH
    static void Cleanup();

    // Generated message map functions
protected:
    //{{AFX_MSG(CWareCacheTools)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG
    //DECLARE_MESSAGE_MAP()

//private Attributes
private:

};
/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_WARECACHETOOLS_H__6DEC4E4B_219C_4DB9_904D_CA50F9F691CE__INCLUDED_)

/*
// DLL入口点(用于处理DLL加载和卸载)  
BOOL APIENTRY DllMain(HMODULE hModule,  
                      DWORD  ul_reason_for_call,  
                      LPVOID lpReserved)  
{  
    switch (ul_reason_for_call)  
    {  
    case DLL_PROCESS_ATTACH:  
        // 初始化临界区(注意:这里应该使用更安全的初始化机制,比如InitOnceExecuteOnce)  
        if (!csInitialized_)  
        {  
            InitializeCriticalSection(&Singleton::cs_);  
            csInitialized_ = true;  
        }  
        // 注意:这里不应该执行与特定进程相关的初始化代码,除非确实需要  
        break;  
  
    case DLL_THREAD_ATTACH:  
        // 通常不需要在这里做任何事情,除非有线程特定的初始化需求  
        break;  
  
    case DLL_THREAD_DETACH:  
        // 通常不需要在这里做任何事情,因为线程退出时会自动清理其资源  
        break;  
  
    case DLL_PROCESS_DETACH:  
        // 在进程退出时清理资源  
        if (csInitialized_)  
        {  
            // 注意:这里应该确保没有其他线程正在使用Singleton实例  
            // 在实际应用中,可能需要更复杂的逻辑来确保安全的清理  
            Singleton::Cleanup();  
        }  
        break;  
    }  
    return TRUE;  
}  
*/
// WareCacheTools.cpp : implementation file
//

#include "stdafx.h"
#include "codebook.h"
#include "WareCacheTools.h"
#include <iostream>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWareCacheTools

    CWareCacheTools* CWareCacheTools::instance_;  
#if USE_MUTEX 
    CMutex CWareCacheTools::_mutex;
#else
    CRITICAL_SECTION CWareCacheTools::cs_;
    volatile bool CWareCacheTools::csInitialized_; 
#endif

void CWareCacheTools::Lock()
{
#if USE_MUTEX 
    CWareCacheTools::_mutex.Lock();
#else
    EnterCriticalSection(&cs_);  
#endif
}

void CWareCacheTools::UnLock()
{
#if USE_MUTEX 
    CWareCacheTools::_mutex.Unlock();
#else
    LeaveCriticalSection(&cs_);  
#endif
}

CWareCacheTools* CWareCacheTools::Instance()  
{  
#if USE_MUTEX 
#else
    InitCriticalSection();
#endif
    try
    {
        Lock();
        if (CWareCacheTools::instance_ == NULL)  
        {  
            CWareCacheTools::instance_ = new CWareCacheTools();  
        }  
    }
    catch(...)
    {
    }
    UnLock();
    return instance_;  
}

void CWareCacheTools::InitCriticalSection()  
{  
#if USE_MUTEX 
#else
    if(!csInitialized_) 
    {
        // 初始化临界区(注意:这里应该使用更安全的初始化机制,  
        // 但VC6.0不支持InitOnceExecuteOnce,所以这里简化处理) 
        //临界区初始化:在VC6.0中,没有InitOnceExecuteOnce函数,所以我们直接在Instance方法中检查并初始化临界区。
        //这可能会导致多个线程同时进入临界区初始化代码(称为“竞态条件”),
        //但在这种情况下,由于InitializeCriticalSection是线程安全的,所以不会有问题。
        //然而,这不是最佳实践,因为它依赖于Windows API的特定行为。
        InitializeCriticalSection(&CWareCacheTools::cs_);  //CCriticalSection 这个类构造函数直接初始化
        csInitialized_ = true;
    }
#endif
}

void CWareCacheTools::RemoveCriticalSection()  
{  
#if USE_MUTEX 
#else
    if (csInitialized_)  
    {  
        DeleteCriticalSection(&CWareCacheTools::cs_);  
        csInitialized_ = false;  
    }
#endif    
}  

void CWareCacheTools::Cleanup()  
{  
    try
    {
#if USE_MUTEX 
        Lock();
#else 
        EnterCriticalSection(&cs_);
#endif

        
        delete instance_;  
        instance_ = NULL;  
    }
    catch(...)
    {
    }
#if USE_MUTEX 
    UnLock();
#else 
    LeaveCriticalSection(&cs_);
    RemoveCriticalSection();
#endif
    
}

// Note: In a real-world DLL, you would also need to export a function to delete the critical section  
// and clean up resources when the DLL is unloaded. However, for simplicity, we'll omit that here.  
// In practice, you might want to use a more advanced synchronization mechanism or a library like Boost.Thread  
// to handle the lifetime of the critical section and other synchronization primitives.

CWareCacheTools::CWareCacheTools()
{
    std::cout << "CWareCacheTools instance created." << std::endl;  
}

CWareCacheTools::~CWareCacheTools()
{
    std::cout << "CWareCacheTools instance destroyed." << std::endl; 
}


//BEGIN_MESSAGE_MAP(CWareCacheTools, CWnd)
    //{{AFX_MSG_MAP(CWareCacheTools)
        // NOTE - the ClassWizard will add and remove mapping macros here.
    //}}AFX_MSG_MAP
//END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CWareCacheTools message handlers

 

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#ifndef __AFXMT_H__
#define __AFXMT_H__

#ifndef __AFX_H__
    #include <afx.h>
#endif

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, off)
#endif
#ifndef _AFX_FULLTYPEINFO
#pragma component(mintypeinfo, on)
#endif

#ifdef _AFX_PACKING
#pragma pack(push, _AFX_PACKING)
#endif

/////////////////////////////////////////////////////////////////////////////
// AFXMT - MFC Multithreaded Extensions (Syncronization Objects)

// Classes declared in this file

//CObject
    class CSyncObject;
        class CSemaphore;
        class CMutex;
        class CEvent;
        class CCriticalSection;

class CSingleLock;
class CMultiLock;

#undef AFX_DATA
#define AFX_DATA AFX_CORE_DATA

/////////////////////////////////////////////////////////////////////////////
// Basic synchronization object

class CSyncObject : public CObject
{
    DECLARE_DYNAMIC(CSyncObject)

// Constructor
public:
    CSyncObject(LPCTSTR pstrName);

// Attributes
public:
    operator HANDLE() const;
    HANDLE  m_hObject;

// Operations
    virtual BOOL Lock(DWORD dwTimeout = INFINITE);
    virtual BOOL Unlock() = 0;
    virtual BOOL Unlock(LONG /* lCount */, LPLONG /* lpPrevCount=NULL */)
        { return TRUE; }

// Implementation
public:
    virtual ~CSyncObject();
#ifdef _DEBUG
    CString m_strName;
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
    friend class CSingleLock;
    friend class CMultiLock;
};

/////////////////////////////////////////////////////////////////////////////
// CSemaphore

class CSemaphore : public CSyncObject
{
    DECLARE_DYNAMIC(CSemaphore)

// Constructor
public:
    CSemaphore(LONG lInitialCount = 1, LONG lMaxCount = 1,
        LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL);

// Implementation
public:
    virtual ~CSemaphore();
    virtual BOOL Unlock();
    virtual BOOL Unlock(LONG lCount, LPLONG lprevCount = NULL);
};

/////////////////////////////////////////////////////////////////////////////
// CMutex

class CMutex : public CSyncObject
{
    DECLARE_DYNAMIC(CMutex)

// Constructor
public:
    CMutex(BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL,
        LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);

// Implementation
public:
    virtual ~CMutex();
    BOOL Unlock();
};

/////////////////////////////////////////////////////////////////////////////
// CEvent

class CEvent : public CSyncObject
{
    DECLARE_DYNAMIC(CEvent)

// Constructor
public:
    CEvent(BOOL bInitiallyOwn = FALSE, BOOL bManualReset = FALSE,
        LPCTSTR lpszNAme = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL);

// Operations
public:
    BOOL SetEvent();
    BOOL PulseEvent();
    BOOL ResetEvent();
    BOOL Unlock();

// Implementation
public:
    virtual ~CEvent();
};

/////////////////////////////////////////////////////////////////////////////
// CCriticalSection

class CCriticalSection : public CSyncObject
{
    DECLARE_DYNAMIC(CCriticalSection)

// Constructor
public:
    CCriticalSection();

// Attributes
public:
    operator CRITICAL_SECTION*();
    CRITICAL_SECTION m_sect;

// Operations
public:
    BOOL Unlock();
    BOOL Lock();
    BOOL Lock(DWORD dwTimeout);

// Implementation
public:
    virtual ~CCriticalSection();
};

/////////////////////////////////////////////////////////////////////////////
// CSingleLock

class CSingleLock
{
// Constructors
public:
    CSingleLock(CSyncObject* pObject, BOOL bInitialLock = FALSE);

// Operations
public:
    BOOL Lock(DWORD dwTimeOut = INFINITE);
    BOOL Unlock();
    BOOL Unlock(LONG lCount, LPLONG lPrevCount = NULL);
    BOOL IsLocked();

// Implementation
public:
    ~CSingleLock();

protected:
    CSyncObject* m_pObject;
    HANDLE  m_hObject;
    BOOL    m_bAcquired;
};

/////////////////////////////////////////////////////////////////////////////
// CMultiLock

class CMultiLock
{
// Constructor
public:
    CMultiLock(CSyncObject* ppObjects[], DWORD dwCount, BOOL bInitialLock = FALSE);

// Operations
public:
    DWORD Lock(DWORD dwTimeOut = INFINITE, BOOL bWaitForAll = TRUE,
        DWORD dwWakeMask = 0);
    BOOL Unlock();
    BOOL Unlock(LONG lCount, LPLONG lPrevCount = NULL);
    BOOL IsLocked(DWORD dwItem);

// Implementation
public:
    ~CMultiLock();

protected:
    HANDLE  m_hPreallocated[8];
    BOOL    m_bPreallocated[8];

    CSyncObject* const * m_ppObjectArray;
    HANDLE* m_pHandleArray;
    BOOL*   m_bLockedArray;
    DWORD   m_dwCount;
};

/////////////////////////////////////////////////////////////////////////////
// Inline function declarations

#ifdef _AFX_PACKING
#pragma pack(pop)
#endif

#ifdef _AFX_ENABLE_INLINES
#define _AFXMT_INLINE AFX_INLINE
#include <afxmt.inl>
#undef _AFXMT_INLINE
#endif

#undef AFX_DATA
#define AFX_DATA

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, on)
#endif
#ifndef _AFX_FULLTYPEINFO
#pragma component(mintypeinfo, off)
#endif

#endif  // __AFXMT_H__

/////////////////////////////////////////////////////////////////////////////

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

// Inlines for AFXMT.H

#ifdef _AFXMT_INLINE

_AFXMT_INLINE CSyncObject::operator HANDLE() const
    { return m_hObject;}

_AFXMT_INLINE BOOL CSemaphore::Unlock()
    { return Unlock(1, NULL); }

_AFXMT_INLINE BOOL CEvent::SetEvent()
    { ASSERT(m_hObject != NULL); return ::SetEvent(m_hObject); }
_AFXMT_INLINE BOOL CEvent::PulseEvent()
    { ASSERT(m_hObject != NULL); return ::PulseEvent(m_hObject); }
_AFXMT_INLINE BOOL CEvent::ResetEvent()
    { ASSERT(m_hObject != NULL); return ::ResetEvent(m_hObject); }

_AFXMT_INLINE CSingleLock::~CSingleLock()
    { Unlock(); }
_AFXMT_INLINE BOOL CSingleLock::IsLocked()
    { return m_bAcquired; }

_AFXMT_INLINE BOOL CMultiLock::IsLocked(DWORD dwObject)
    { ASSERT(dwObject >= 0 && dwObject < m_dwCount);
         return m_bLockedArray[dwObject]; }

_AFXMT_INLINE CCriticalSection::CCriticalSection() : CSyncObject(NULL)
    { ::InitializeCriticalSection(&m_sect); }
_AFXMT_INLINE CCriticalSection::operator CRITICAL_SECTION*()
    { return (CRITICAL_SECTION*) &m_sect; }
_AFXMT_INLINE CCriticalSection::~CCriticalSection()
    { ::DeleteCriticalSection(&m_sect); }
_AFXMT_INLINE BOOL CCriticalSection::Lock()
    { ::EnterCriticalSection(&m_sect); return TRUE; }
_AFXMT_INLINE BOOL CCriticalSection::Lock(DWORD /* dwTimeout */)
    { return Lock(); }
_AFXMT_INLINE BOOL CCriticalSection::Unlock()
    { ::LeaveCriticalSection(&m_sect); return TRUE; }

#endif //_AFXMT_INLINE

 

 

 

posted on 2013-01-29 11:05  不及格的程序员-八神  阅读(278)  评论(0编辑  收藏  举报