我正在尝试更多地设计模式,我正在构建一个快速测试来帮助我进一步理解单例模式。然而,我在.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 :(得分:5)
很抱歉,但是Singleton不是关于线程安全的。它只有一个对象的实例。
因此PopulateCrudItemsProcess
和GetStrValue
不是线程安全的......
答案 1 :(得分:2)
您可以尝试使用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();
}
}
}
答案 2 :(得分:1)
关于您的情况下的线程安全性,您必须介于:
之间第一部分是隐蔽的,静态构造函数只在多个线程中调用一次,因此静态变量的所有初始化代码也只执行一次。这是隐含的,并且本着使事情易于理解的精神,你应该使用更明确的方法在get实例方法中进行初始化,这更常见,见下文。但是,为了让您的代码更容易为其他人阅读,它已经在这方面起作用了。
第二部分本身也是线程安全的,因为你没有像JonSkeet所指出的那样共享任何资源。
第三部分似乎是其他人已经指出的问题。同时执行StoredProcedures或任何SQL会导致数据库级别的死锁。在你的情况下很可能它会发生,这是你看到的错误。
从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;
}
}