解决多线程环境下的静态缓存问题

多线程应用程序中,静态缓存和静态对象的使用常常会导致一系列问题,如线程安全问题、数据不一致等。这些问题在高并发环境下尤为明显,例如在多用户访问门户网站时。本文将探讨一种有效的解决方案,即通过为每个线程创建独立的缓存来避免这些问题。

问题的提出

在开发一个门户网站时,遇到了两个主要问题:

  • 当多于一个用户同时访问时,系统会失败,报错信息通常涉及到集合被修改等。
  • 当设置多个IIS工作进程时,相同的页面会返回不同的数据。

这些问题的根源在于静态缓存和静态对象的使用。静态对象对于所有线程来说是相同的,但对于每个进程来说是不同的,这导致了多线程操作中的冲突,并阻止了SQL Server的查询,因为它“认为”数据已经被读取过了。

尝试的解决方案

由于时间和资源的限制,没有机会重新设计和编码门户网站。因此,需要一种清晰而强大的解决方案。

首先,尝试了传统的同步方法,试图同步关键代码块。但是,如果有一个静态的集合集合,这并不是真的可行。所有嵌套的集合都有可能在代码的任何部分失败。

接下来,尝试不枚举原始列表。在枚举之前,总是创建一个副本,以防止在枚举完成之前修改它。然而,这导致了一系列的新问题,不得不将一切恢复原状。

创新的解决方案

然后,一个新想法突然闯入了脑海。现在看起来这似乎很明显,但它花了很长时间才出现...

看,如果有以下形式的静态缓存

static Hashtable Objects = new Hashtable();

不需要同步它。对于每个线程,只需要为它创建自己的缓存。可以使用CallContext轻松实现这一点。首先,创建一个线程安全的“静态”对象存储:

public sealed class CallObjects { public enum Names { STAT_OBJ_1, STAT_OBJ_2, // ... } public static object Get(Names name) { return CallContext.GetData(name.ToString()); } public static void Set(Names name, object obj) { if (obj == null) CallContext.FreeNamedDataSlot(name.ToString()); else CallContext.SetData(name.ToString(), obj); } }

现在可以使用静态属性返回线程依赖的Hashtable:

Hashtable Objects { get { Hashtable objects = (Hashtable)CallObjects.Get(CallObjects.Names.STAT_OBJ_1); if (objects == null) { objects = new Hashtable(); CallObjects.Set(CallObjects.Names.STAT_OBJ_1, objects); } return objects; } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485