为什么我的asp.net代码中多次创建静态对象?

dax*_*axu 4 c# windbg stackexchange.redis

我认为静态对象是跨多个线程共享的.但是,我在我的一个网站上遇到了一个高CPU问题,所以我采取了一个windbg转储并且非常惊讶,我看到了:

在此输入图像描述

我们可以看到有一个名为ConnectionMultiplexer的类的10个实例.但我的代码将ConnectionMultiplexer创建为静态对象.这应该意味着只为所有线程创建一个实例.那么windbg怎么会出现多个实例呢?

这是我创建redis连接的代码

public static class CacheConnection
    {
        private static StackExchangeRedisCacheClient _newconnectionDb;

        public static StackExchangeRedisCacheClient NewConnectionDb
            => _newconnectionDb ?? (_newconnectionDb = NewRedisConnection());

        private static IDatabase _connectionDb;

        public static IDatabase ConnectionDb => _connectionDb ?? (_connectionDb = RedisConnection());

        private static StackExchangeRedisCacheClient NewRedisConnection()
        {
            var serializer = new NewtonsoftSerializer();
            return new StackExchangeRedisCacheClient(Connection, serializer);
        }

        private static IDatabase RedisConnection()
        {
            var cacheDatabase = Connection.GetDatabase();
            return cacheDatabase;
        }

        public static ConnectionMultiplexer Connection => LazyConnection.Value;

        private static readonly Lazy<ConnectionMultiplexer> LazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(
            System.Configuration.ConfigurationManager.AppSettings["CacheConnectionString"]), LazyThreadSafetyMode.PublicationOnly);
    }
Run Code Online (Sandbox Code Playgroud)

Mih*_*kov 5

ConnectionMultiplexer实际上是一个readonly(get)属性,使用新的C#7 short syntax =>,LazyConnection.Value每次访问它时都会返回.

然后你使用LazyThreadSafetyMode.PublicationOnlyMSDN中定义的那个(https://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode(v=vs.110).aspx)

当多个线程尝试同时初始化Lazy实例时,允许所有线程运行初始化方法(或者如果没有初始化方法,则运行默认构造函数).完成初始化的第一个线程设置Lazy实例的值.除非初始化方法在这些线程上抛出异常,否则该值将返回到同时运行初始化方法的任何其他线程.由竞争线程创建的任何T实例都将被丢弃.如果初始化方法在任何线程上抛出异常,则异常将从该线程上的Lazy.Value属性传播出去.该异常未缓存.IsValueCreated属性的值保持为false,并且后续调用Value属性(由抛出异常的线程或其他线程)导致初始化方法再次运行.如果初始化方法以递归方式访问Lazy实例的Value属性,则不会抛出异常.

这意味着如果多个线程试图在同一时间访问它们,它们将各自创建自己的实例,尽管您最终将使用创建的第一个线程而不管线程(但实例仍然存在).

你真正需要的是LazyThreadSafetyMode.ExecutionAndPublication 但这可能会引入死锁.

如果你不需要这个懒惰你可以使用Jon Skeet在他的书C#In Depth中提出的单例模式实现之一

你可以在http://csharpindepth.com/Articles/General/Singleton.aspx找到它们