如何轻松地模拟这种单例模式的非线程安全性

本文关键字:线程 安全性 单例模式 何轻松 模拟 | 更新日期: 2023-09-27 18:19:19

根据Jon Skeet的文章,下面的模式是不好的,因为它不是线程安全的。

// Bad code! Do not use!
public sealed class Singleton
{
    private static Singleton instance = null;
    private Singleton()
    {
    }
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

我还没有学过线程,所以它对我来说有点抽象。你能给我一个简单的代码来模拟线程问题(当问题发生时我们得到通知)?

如何轻松地模拟这种单例模式的非线程安全性

这很简单,只是让一些东西并行访问你的单例中的属性,例如像这个控制台应用程序。

class Program
{
    static void Main(string[] args)
    {
        var threads = Enumerable.Repeat(new Action(() => Console.WriteLine(Singleton.Instance.guid)), 10);
        Parallel.ForEach(threads, t => t());
        Console.Read();
    }
}

(我已经添加了一个guid属性到你的类来测试)

public sealed class Singleton
{
    public Guid guid = Guid.NewGuid();
    private static Singleton instance = null;
    private Singleton()
    {
    }
    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

这个单例实现的问题是两个线程可以同时访问getter,每个线程都将创建一个新实例。因此,第一个线程可能最终与第二个线程具有不同的实例…这会导致意想不到的行为。

这只是对OP评论的回复:

    static void Main(string[] args)
    {
        int test = 5;
        Task<Singleton>[] arr =
        { 
            Task<Singleton>.Factory.StartNew(() => Singleton.Instance),
            Task<Singleton>.Factory.StartNew(() => Singleton.Instance),
        };
        Task.WaitAll(arr);
        foreach (var item in arr)
        {
            Singleton s = item.Result;
            s.MyProperty = test++;
            Console.WriteLine(s.MyProperty);
        }
    }

MyProperty只是我添加的一个int属性。