在多线程应用程序中,静态缓存和静态对象的使用常常会导致一系列问题,如线程安全问题、数据不一致等。这些问题在高并发环境下尤为明显,例如在多用户访问门户网站时。本文将探讨一种有效的解决方案,即通过为每个线程创建独立的缓存来避免这些问题。
在开发一个门户网站时,遇到了两个主要问题:
这些问题的根源在于静态缓存和静态对象的使用。静态对象对于所有线程来说是相同的,但对于每个进程来说是不同的,这导致了多线程操作中的冲突,并阻止了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;
}
}