2012-03-17 97 views
0

我试图让设计模式更多一点,我正在建立一个快速测试,以帮助我进一步了解单身模式。然而,我在.net中遇到了一个让我感到困惑的错误,是什么使得它更加怪异,我无法复制这个刚刚添加到我的混淆中的错误。下面的代码并不是世界上最好的代码,但我随机测试了一些东西,以帮助我深入了解。与单身人士死锁错误

class Program 
{ 

    static void Main(string[] args) 
    { 
     Thread t1 = new Thread(new ThreadStart(run1)); 
     Thread t2 = new Thread(new ThreadStart(run2)); 

     t1.Priority = ThreadPriority.Lowest; 
     t1.Start(); 
     t2.Priority = ThreadPriority.Lowest; 
     t2.Start(); 

     Console.ReadLine(); 
    } 

    public static void run1() 
    { 
     Console.WriteLine("im in run1 \n"); 
     TestSingleton._instance.PopulateCrudItemsProcess(); 
     Thread.Sleep(1000); 
     Console.WriteLine(TestSingleton._instance.GetStrValue("first", 1000)); 
    } 
    public static void run2() 
    { 
     Console.WriteLine("im in run2 \n"); 
     TestSingleton._instance.PopulateCrudItemsProcess(); 
     Console.WriteLine(TestSingleton._instance.GetStrValue("second", 500)); 

    } 

} 

sealed class TestSingleton 
{ 
    private TestSingleton() { } 

    public static readonly TestSingleton _instance = new TestSingleton(); 

    public string GetStrValue(string str, int time) 
    { 
     return str; 
    } 

    public void PopulateCrudItemsProcess() 
    { 
     const string proc = "[testdb].[dbo].[tstsproc]"; 
     string _reportingConnStr = ConfigurationManager.ConnectionStrings["reporting"].ConnectionString; 

     using (SqlConnection conn = new SqlConnection(_reportingConnStr)) 
     { 
      using (SqlCommand cmd = new SqlCommand(proc, conn)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.CommandTimeout = 7200; 
       conn.Open(); 
       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
} 

现在发生了什么是该程序崩溃的cmd.ExecuteNonQuery();并表示资源存在僵局。我希望我可以捕捉到实际的错误,但就像我说我只能得到一次错误。根据MS这个代码是一个线程安全的方式来创建一个单身人士。那么对于可能导致错误的任何想法?任何信息非常感谢。

+0

此错误可能来自数据库。你的ExecuteNonQuery在做什么? – 2012-03-17 08:44:42

+0

http://support.microsoft.com/kb/832524 – 2012-03-17 09:00:59

回答

2

你可以尝试使用lock关键字。
它允许开发人员制定必须在应用程序线程之间同步的代码范围。 这是为了确保传入的线程在完成之前不会中断当前的线程。

因此,让我们一个例子:

声明你的单身里面这样

lock(_oLock) 
    { 
     using (SqlConnection conn = new SqlConnection(_reportingConnStr)) 
     { 
      using (SqlCommand cmd = new SqlCommand(proc, conn)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.CommandTimeout = 7200; 
       conn.Open(); 
       cmd.ExecuteNonQuery(); 
      } 
     } 
    } 
+0

看到@Skeet和@Hans的评论后,我不确定你的问题是否被阻止。你能显示你的''[testdb]的代码吗?[dbo]。[tstsproc]“;'如@NikolaMarkovinović所说的 – Steve 2012-03-17 09:18:11

5

对不起,但Singleton并不是线程安全的。它关于只有一个对象的实例。

因此PopulateCrudItemsProcessGetStrValue是不是线程安全的...

+0

嗯,但他们是。该代码不会生成异常。 – 2012-03-17 09:02:38

+2

它们*在单例方面是*线程安全的。在课堂上没有共享状态 - 嘿,这些方法可以变成静态的。你认为他们是不是线程安全的? (我希望存储过程可能会失败,因为试图在两个线程中并发地填充数据库,但这不同于这些方法本身不是线程安全的。如果尝试运行,您会得到相同的结果两个独立的*进程同时使用同一个方法)。它们不是线程安全的*因为它是单例*当然,但这是另一回事。 – 2012-03-17 09:11:09

1

关于您的情况线程安全的,你有间devide全球

private object _oLock; 

重写PopulateCrudItemsProcess:

  • 创建辛格尔顿
  • 执行的单一实例
  • 方法执行存储过程

你有隐蔽的第一部分,静态构造函数只有在多个线程调用一次,因此所有的初始化代码对于静态变量只能执行一次。这是隐含的,但本着让事情易于理解的精神,您应该使用更明确的方法,并在更常见的get实例方法中进行初始化,请参见下文。然而,让你的代码更容易被别人阅读,它已经在这方面的工作。

第二部分本身也是线程安全的,因为您没有像JonSkeet指出的那样共享任何资源。

然而,第三部分似乎是其他人已经指出的问题。同时执行StoredProcedures或任何SQL可能导致DB级别的死锁。你的情况很可能是这样,这是你所看到的错误。

盗窃KarlLynch存根,使实例创建明确的线程安全:

public class MySingleton 
{ 
    private static MySingleton Instance{ get; set; } 
    private static readonly object initLock; 

    // Private constructor 
    private MySingleton() 
    { 
    initLock = new object(); 
    } 

    public static MySingleton GetInstance() 
    {  
    if (Instance == null) 
    { 
     lock(initLock) 
     { 
     if (Instance == null) 
     { 
      Instance = new MySingleton(); 
     } 
     } 
    }  
    return Instance; 
    } 
}