如何使这个c#类成为单例,线程安全

时间:2012-03-30 02:07:30

标签: c# asp.net .net

所以这就是我想要做的事情:

public static class MyClass
{

   private Dictionary<string,IDisposable> dict = null;

   static MyClass()
   {
         dict = new Dictionary<string,IDisposable>();

         // i fill up the dictionary in a loop here
         //
   }

   public static UseDictionary(string name)
   {
       var obj = dict[name]        
       obj.DoSomeAction();           
   }

}

static void Main(string[] args)
{
   Parallel.For(0, 1000, x =>
   {               
       // assuming 'name' does exist in the dictionary 
       MyClass.UseDictionary("name"); // randomly throws null reference errors 
   });
}

我基本上想要这个类的一个实例,它只会初始化一次字典(IDisposable项目是一个昂贵的远程连接,我打开它,我想只打开一次)

此类将由不同的asp.net页面使用。所以我希望它是线程安全和单身。我想在静态构造函数中初始化字典将使它只被调用一次并且是线程安全的,不知道为什么我一直收到错误。有什么想法吗?

4 个答案:

答案 0 :(得分:2)

我同意@Leonardo Garcia Crespo的评论。从我在这里看到的,你只在静态构造函数中写入字典,每个应用程序域只会调用一次。其他所有内容都是读取,如果未修改集合,则通用字典支持多个读取器。问题只在于被调用的代码。它还需要是线程安全的。或者,您可以使用另一个关联锁字典并锁定相应的锁,这样您一次只能调用一个非线程安全的实例。

public static class MyClass
{
    public static Dictionary<string,SomeDisposableClass> dict = null;
    public static Dictionary<string,object> locks = null;

    static MyClass()
    {
         // populate the dictionary 
         locks = new Dictionary<string,object>();
         foreach (var key in dict.Keys)
         {
             locks[key] = new object();
         }
    }

    public static void UseDictionary( string name )
    {
        var obj = dict[name];
        var sync = locks[name];
        lock(sync)
        {
            obj.DoSomething();
        }
    }
}

您也可以使用Singleton模式,但如果您只有一个可能不需要的静态方法。我会注意到,使用这个类的任何代码都可以解决这个问题。

而且,是的,我知道如果用name的相同值调用1000次,它将是单线程的。但是,如果被调用的方法不是线程安全的,那么这是你能做到的最好的方法。这样,至少,您可以让多个线程同时针对name的不同值运行,因为它们会锁定不同的对象。

答案 1 :(得分:0)

请看http://www.codeproject.com/Articles/14026/Generic-Singleton-Pattern-using-Reflection-in-C

抱歉,'(通往地狱的道路充满了良好的意图

我只想提供另一个简单的选项而不需要详细介绍

我也遇到问题之后的时间,我发布Multi web services so multi singleton最终创建我自己的版本。

这真的有用,我有大约50个内部应用程序和一些外部提供商使用它。

总结一下 在检查了关于单例模式和在c#i中完成的不同示例的想法后得出结论,一个好的实现基于内部类创建一个实例是保证尽管调用了单例clas,但是一次调用构造函数从许多线程,所以只有一个实例

在vb.net简化

Public NotInheritable Class ServiceProxySingleton

 Private Sub New()
 End Sub

 Public Shared Function GetInstance() As ServiceProxySingleton
   Return NestedSingletonService._instance
 End Function

  ' Internal class
  Class NestedSingletonService
    Friend Shared ReadOnly _instance As [SingletonClass] = New [SingletonClass]()
    Shared Sub New()
    End Sub
  End Class

End Class

答案 2 :(得分:0)

以下代码应该是线程安全的,因为.NET保证了静态字段初始化程序的线程安全性

private static readonly Dictionary<string,IDisposable> dict = 
                                           CreateAndInitializeDictionary();
private static Dictionary<string,IDisposable> CreateAndInitializeDictionary() {
   var d = new Dictionary<string,IDisposable>();
   .... // here add items 
   return d;
}

在再次检查您的代码之后,我意识到您的实现也是线程安全的。问题应该在DoSomething() :)

中的某个地方

答案 3 :(得分:0)

您可以在班级上使用“同步”属性。这就是我为我的一个班级所做的。我把评论包括在内,因为它们解释了这种情况。

[Synchronization]
public class IprServerController : ContextBoundObject
{
    // We need to worry about things like a user deleting a route at the same time that another user is starting one.
    // So, instead of trying to lock appropriately throughout the class, we can lock declaratively, by using the
    // Synchronization attribute and deriving from ContextBoundObject. Now only one thread can execute methods on
    // this class at a time.