我遇到了一些像这样的代码。
public class ConnectionUtility
{
private static SqlConnection con;
public static SqlConnection GimmeConnection()
{
if(con==null)
con = new SqlConnection();
return con;
}
}
这是在ASP.NET Web应用程序中。可以预期会有竞争条件,其中一个请求/页面尝试打开/关闭连接上的执行事物,而其他请求也尝试执行这些操作吗?
答案 0 :(得分:4)
是的,那里肯定有竞争条件。多个线程可能同时调用GimmeConnection()
并且都创建一个新连接,因此con
的初始化存在竞争条件。
在.NET中执行此操作的正确方法实际上非常简单:
public class ConnectionUtility
{
private static SqlConnection con = new SqlConnection();
public static SqlConnection GimmeConnection()
{
return con;
}
}
这保证可以正常工作,没有任何竞争条件。
当然,还有另一种可能的竞争条件,这不是由此确定的:
由于您创建了一个可全局访问的连接,因此多个线程可以轻松地同时使用它,这是不安全的。
如果连接由多个线程使用,则必须显式序列化这些访问,例如通过锁定。
当然,最佳行动方案仍然只是avoid singletons ......
答案 1 :(得分:1)
是的...您不应该在多个线程之间共享一个静态连接。这不仅仅是竞争条件问题 - 您将有许多网页试图同时使用相同的连接,这将无法正常工作。
您可以在“con”上使用[ThreadStatic]属性,这将使其在线程范围内为全局。
答案 2 :(得分:1)
是的,你可以期待这样的竞争条件。很好的发现!
答案 3 :(得分:1)
两种竞争条件,一种可能是无害的,一种是可怕的。还有一个逻辑错误。
潜在无害的一个是构造函数逻辑:
if(con==null)
con = new Thing();
如果你想使用单个对象作为优化,这可能是无害的,但是使用一个多于一个的句点仅仅是次优的而不是错误的(不是每个种族都是世界末日)。这取决于Thing
是什么以及它是如何使用的。
灾难性的种族是:
return con;
因为在这种情况下,类型是SqlConnection
,这不是线程安全的,所以每次使用该属性都是一场惹恼灾难的种族。
逻辑错误在于让单例缓存成为一个很轻的对象,它可以处理它自己的重要部分的线程安全池。由于这种合并,你不应该坚持SqlConnection
超过绝对必要的时间。实际上,如果你在一次使用和另一次使用之间存在差距(虽然本身就是一种难闻的气味),你应该关闭它并再次重新打开它。这使得SqlConnection
为您提供的线程安全池,在使用之间以最佳方式工作。
答案 4 :(得分:0)
每当写单身时,你应该让它们成为线程安全的:
class Singleton
{
private Singleton() { }
private static volatile Singleton instance;
public static Singleton GetInstance()
{
// DoubleLock
if (instance == null)
{
lock(m_lock) {
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
// helper
private static object m_lock = new object();
}