单例的死锁错误
本文关键字:错误 死锁 单例 | 更新日期: 2023-09-27 18:28:17
我正在努力深入设计模式,我正在构建一个快速测试来帮助我进一步理解单例模式。然而,我在.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的说法,这段代码是创建单例的线程安全方式。那么,你有没有想过是什么导致了这个错误呢?如有任何信息,我们将不胜感激。
因此PopulateCrudItemsProcess
和GetStrValue
不是线程安全的。。。
您可以尝试使用lock
关键字
它允许开发人员确定必须在应用程序线程之间同步的代码范围。这是为了确保传入线程在电流结束之前不会中断电流。
举个例子:
在您的单例中声明全局
private object _oLock;
以这种方式重写PopulateCrudItemsProcess
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();
}
}
}
在您的情况下,关于线程安全,您必须在以下两者之间进行区分:
- 创建Singleton
- 在singleton实例上执行方法
- 执行存储过程
第一部分是隐蔽的,静态构造函数在多个线程中只调用一次,因此静态变量的所有初始化代码也只执行一次。不过,这是隐含的,为了使事情易于理解,您应该使用更明确的方法,也可以在更常见的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;
}
}