垃圾回收,我们应该依赖它吗?

6
在代码中,我们有以下内容:
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ToString()))
{
    cn.Open();

    // Set all previous settings to inactive
    using (SqlCommand cmd = new SqlCommand("UPDATE tblSiteSettings SET isActive = 0", cn))
    {                            
        cmd.ExecuteNonQuery();
    }

    cn.Close();
}

cn.close 在技术上并不是必需的,因为垃圾回收会为我们处理连接。

然而,我总是喜欢关闭它,不要依赖垃圾回收。这样做是不好的吗?是浪费时间吗?还是认为不依赖自动化是良好的实践?

提前感谢您的想法和意见。我将把它标记为社区wiki,因为这可能是主观的。


7
因为您正在使用 using 语句,所以 cn.Close() 是完全多余的。 - Matt Ellen
4
在你的例子中,是IDispose / using()清理连接,而不是GC。控制资源(如连接和文件)的生命周期是很好(必要)的实践方法。只有对于内存资源,才应该依赖于GC。 - tenfour
2个回答

15

您不应该依赖垃圾回收来处理这个。雷蒙德·陈的博客文章是一个很好的起点。如果您不手动Close/Dispose连接,那么就没有保证它会发生,因为否则只有在从Finalizer调用Dispose时才会发生,而这可能永远不会发生:

一个正确编写的程序不能假定finalizers会在程序终止之前的任何时间点运行。

实际上,您的连接的终结器最终可能会发生,但即使如此,您仍然会持有一个比实际需要更长时间的活动连接。如果数据库一次只允许有限数量的活动连接,这可能会产生问题。

您正在执行的操作被认为是良好的实践:当您完成资源使用后,请释放它们。如果一个对象是IDisposable,请尽早Dispose它。


关于数据库连接,我前几个月被同事的代码所困扰。他只是把所有的清理工作留给了 finalizer,这在小的数据库上运行得很完美,但是在大型数据库上由于进程退出时 finalizer 的时间限制而“突然”失败了。更多细节请参见:http://nitoprograms.blogspot.com/2009/08/finalizers-at-process-exit.html - Stephen Cleary
请注意,Chen 表示 finalizer 甚至在应用程序退出时都不能保证被调用。 - Tergiver

6

首先,在你的例子中,你使用了与GC无关的IDisposable接口。实际上,你的代码编译成了这样:

SqlConnection cn = null;
try
{
    cn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ToString());
    cn.Open();

    // Set all previous settings to inactive
    using (SqlCommand cmd = new SqlCommand("UPDATE tblSiteSettings SET isActive = 0", cn))
    {
        cmd.ExecuteNonQuery();
    }
    cn.Close();
}
finally
{
    if ( cn != null )
        cn.Dispose();
}

cn.Dispose()cn.Close()在这种情况下是相同的,因此后者是多余的。

现在,如果您想谈论垃圾回收,那么可以这样写:

    SqlCOnnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalSqlServer"].ToString());
    cn.Open();

    // Set all previous settings to inactive
    using (SqlCommand cmd = new SqlCommand("UPDATE tblSiteSettings SET isActive = 0", cn))
    {
        cmd.ExecuteNonQuery();
    }
    cn = null; // Or just leave scope without doing anything else with cn.

在这种情况下,垃圾回收将关闭已打开的连接并将其返回到池中。在这种情况下,这意味着您不能预测何时会发生这种情况。实际上,这段代码很可能会泄漏SqlConnection,并且很快就会用完它们(因为池中没有可用的连接,所以您会得到一个TimeoutException)。
因此,是的,您上面的示例是正确的方法。每当使用实现IDisposable接口的对象时,请将其包装在using块中。您不需要担心.Close(),虽然它也没有什么坏处。我个人不写它。少写代码,少出错。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接