C# 多线程调用单例类时的安全性问题

勇哥注:

C# 多线程调用单例类时的安全性问题是非常值得我们重视的,出现类似的问题会造成软件出现偶发问题,非常隐蔽。

单例类造成的问题主要有两方面: 一是你的单例到底是不是单例? 二是你的单例是不是在被多线程调用。


第一个问题请参考:

C#实现单例模式的几种方法总结 http://www.skcircle.com/?id=1924


第二个问题请参考下面的说明:


多个线程,同时调用单例的同一个方法会出现什么现象?
阻塞? 并不会!!!
属性是安全的? 也不是!!!
总结就是:
多线程调用单例的同一个方法
线程不会阻塞,数据也不安全


普通的单例模式是线程不安全的,验证方法如下:

sealed class Singleton1
    {
        private Singleton1()
        {
        }
        private static Singleton1 instance = null;

        public static Singleton1 Instance
        {
            get
            {
                if (instance == null)
                {
                    Console.WriteLine("Cup");
                    instance = new Singleton1();
                }
                return instance;
            }
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            Singleton1 st1 = null;
            Singleton1 st2 = null;

            while (true)
            {
                Thread t1 = new Thread(() =>
                {
                    st1 = Singleton1.Instance;
                });

                Thread t2 = new Thread(() =>
                {
                    st2 = Singleton1.Instance;
                });
                t1.Start();
                t2.Start();
                Thread.Sleep(100);
            }
        }
    }


如上所示,打印的结果有很大概率出现两次"Cup",说明两个线程都创建了新的对象,单例被打破了。

改进方式:加锁

sealed class Singleton1
    {
        private Singleton1()
        {
        }

        private static readonly object syncObj = new object();
        private static Singleton1 instance = null;

        public static Singleton1 Instance
        {
            get
            {
                lock (syncObj)
                {
                    if (instance == null)
                    {
                        Console.WriteLine("Cup");
                        instance = new Singleton1();
                    }
                }
                return instance;
            }
        }

        public void Clear()
        {
            instance = null;
        }
    }


运行结果只有一个"Cup",程序在进入代码段时首先判断有没有加锁,如果没有就加锁,另一个线程判断代码已经有锁了,就直接返回,从而保证了单例的唯一性。

缺点:判断锁状态和尝试加锁操作比较消耗性能

改进:锁前判断:

public static Singleton1 Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncObj)
                    {
                        if (instance == null)
                        {
                            Console.WriteLine("Cup");
                            instance = new Singleton1();
                        }
                    }
                }
                return instance;
            }
        }


如此,就只有第一次试图创建实例时要加锁

有没有不用锁的办法呢,也有:

sealed class Singleton1
    {
        private Singleton1()
        {
        }
        private static Singleton1 instance = new Singleton1();

        public static Singleton1 Instance
        {
            get
            {
                if (instance != null)
                {
                    return instance;
                }
                else
                {
                    return null;
                }
            }
        }
    }


C#在调用静态构造函数时初始化静态变量,运行时能够确保只调用一次静态构造函数。但这种机制不确定在其他语言中也存在

如果局限于C#中,还有更优化的方法:

sealed class Singleton1
    {
        private Singleton1()
        {
        }

        public static Singleton1 Instance
        {
            get
            {
                return Nested.instance;
            }
        }

        class Nested
        {
            static Nested()
            {
            }
            internal static readonly Singleton1 instance = new Singleton1();
        }
    }

将创建实例的时机局限在获取Instance时。


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年5月    »
12345
6789101112
13141516171819
20212223242526
2728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864