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); // 释放锁 }
复制代码 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:
,release
,retain
,retainCount
, andautorelease
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
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
来,骚年,给你个实例,我刚被坑过的:
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。
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.
{
public:
static T& getInstance()
{
static T instance;
return instance;
}
};
c++11如何实现单例模式?
还请大大从简单到复杂的多线程、析构函数等等介绍一下c++11的单件模式
单例version1
单例是一个很好理解的概念,即保证类在全局只有一个实例,并提供一个该实例的全局访问点。
代码实现起来思路也很简单,我们先来写个最简单的版本
我们怎么才能实现全局只有一个实例呢?那就是让用户无法自己定义一个Singleton对象。
将构造函数,拷贝构造,拷贝赋值运算符都设为私有成员,这样外界便无法通过构造函数创建新的实例。
之后便是给外界提供一个接口,方便外界获取全局唯一的一个实例。
我们可以用静态变量实现,设置一个singleton指针instance方便未来存储唯一singleton实例。
这里还有一个知识点,无论是静态成员还是静态方法,其存储位置都和singleton类割裂开的。
虽然静态变量声明在类内部,但是存储空间不一样,静态成员会在编译阶段存在静态区,如果有初始化,会放在data段,如果无初始化,会放在bss段。
像a,b这种普通成员,会根据定义类的位置,存储在栈空间或者堆上。
既然静态成员的存储位置和普通成员分开,自然普通成员方法无法“看到”类的静态成员,我们提供给外界的访问api也必须用静态成员方法,也就是上述的GetInstance()。
最后,c++还有个比较容易遗忘的规则,静态成员的初始化必须放在类外。(也就是代码最后一行)
饿汉/懒汉模式
单例的实现有饿汉,懒汉之分。
version1的代码实现的是饿汉模式,即在单例定义的时候(main函数之前)就行实例化,这样的好处是线程安全,在多线程的环境下无论哪个线程通过GetInstance()获得的都是同一个实例(同一个地址)
坏处是浪费了额外的存储空间,若我们导入一个库,其中定义了饿汉模型的单例类,那么即便我们后面不用这个单例,其依旧会实例化,占用空间。
那么我们当然希望这个单例类在整个程序中第一次用到的时候才实例化,不用不实例化,这种模式就是懒汉模式,我们将version1的代码浅修改一下。
我们完成了一个最简单的懒汉模式。instance指针初始化为nullptr
当我们第一次调用GetInstance()方法,此时发现instance指针是空指针,就会new一个Singleton对象并返回。
单例version2
懒汉模式的单例有个极大的问题。它是线程不安全的,假如多个线程同时调用GetInstance()发现instance指针是空指针,于是都开始new一个Singleton,这就会造成单例的崩坏,而且容易导致内存泄漏。
同时new并不是一个原子操作,哪怕在高层语言,都非原子,new操作包括了三个步骤:
所以一个线程在任何一个过程都有可能被其它线程打断。会造成各种问题。
下面代码利用了双重检测+互斥锁+atomic变量+ c++11内存屏障实现了线程安全懒汉单例。
如果你对c++11的atomic和内存屏障有所疑惑,可以看我写的这篇前导文章,希望有所帮助。
玉米:多线程(一):C++11 atomic和内存序
我们来逐个分析这个代码用到的机制。
将instance定义成c++11的atomic类型变量,并使用load和store接口进行instance的读和写,就能保证读和写都是原子操作,即在多线程环境下读写是可靠的,不会在过程中被其它线程给打断。
当我们要new第一个实例并赋值给instance的时候,我们自然希望这整个过程是只有唯一一个线程在进行的,同时整个过程并不方便做成一个原子操作,所以在这里使用互斥锁。当我们获取到instance为nullptr的时候加锁,当我们完成实例化后解锁。
双重检测为了弥补互斥锁的缺陷:也就是某个线程如果拿不到锁,就会阻塞。直到其它线程释放了锁,会重新恢复执行。
想象一个场景,线程t1第一个拿到锁,并给instance实例化,而其它线程t2,t3拿不到锁,陷入阻塞,当t1实例化完,t2拿到锁,此时t2会从获取锁代码处继续往下执行,继续给instance实例化,然而此时instance已经被t1实例化了,多次实例化自然有问题,所以在获取锁以后,需要再次判断instance是否为nullptr,这就是所谓的双重检测。
单例 version3
上面的代码虽然实现了线程安全懒汉单例,但是用的机制太多也很麻烦。那么有没有更简单的方法呢?
答案是还真有,我们可以利用c++11的静态局部变量来实现线程安全懒汉单例,
代码如下:
首先,静态局部变量通过静态方法获得,且只有当该静态方法第一次执行的时候才会初始化,且后续再次调用该方法也不会再次初始化,这是静态局部变量的特性。
而在c++11以上标准中,静态局部变量初始化是线程安全的。
当其中一个线程初始化instance的时候,会阻塞其它线程的初始化行为。
更多回答
c++11标准做法.
没错 c++11 局部静态变量已经是线性安全了
缺一句:private: Singleton(){}
还有拷贝赋值、移动构造、移动赋值通通=delete
private:
Singleton ()= default;
~Singleton ()= default;
Singleton (const Singleton &)= delete;
Singleton & operator=(const Singleton &)= delete;
《Best of All》在跨DLL的时候,不同DLL内各有一份实例耶。严格意义上上,这个不算单例啊。
编译都通不过
什么编译器
头文件里面的static 函数会不会被隐式内联,实际上每个每个包含了这个头文件的地方都有一份实现?
这样写是安全的
Singleton s = Singleton::getInstance() 会执行拷贝初始化,这样不就产生两个实例了吗?
虽然时间有点久了,但还是回一下:Singleton &s = Singleton::getInstance() 可以获取引用;析构函数就正常写要做什么就好了(比如清理资源,关闭文件等)
意思是不是把s声明成引用类型就可以了?还有类的析构函数又该如何定义呢?
函数前面的static是不是不一定需要?这个static只是用于内部链接吗
如果是类的成员函数的话才要前面加static吧?
全局函数加不加static不是对外可不可见的区别吗?
void test() {
static int a = 0;
a++;
cout << a << endl;
}
int main() {
test();
test();
test();
}
这段代码可以正常使用
vs2015才支持,
如果你只是需要全局访问,把一堆函数跟变量直接放进某个新 namespace 中就行。连 class 都不需要定义。
namespace 就是C++语言级天生的单例。你有没有想过单例根本不需要是 class?
之所以有单例模式这种东西,本质上是因为Java没有全局变量。
都写C++了,就不必坚持用class来制造单例了。
除非你需要使用的是某个系统级,无法重复获取的东西。
补充:如果需要控制某个变量的初始化时机,怎么办?答案是把它作为这个namespace的私有变量,自行控制其初始化进程。然后增加相关函数用于主动触发相关初始化。