.NET、SqlConnection对象和多线程

13

我们有一个应用程序,它使用 SQL Server 2008 R2 数据库。在应用程序中,使用 SqlConnection 对象调用数据库。

这个 SqlConnection 对象只会被初始化一次,第一次访问时,并在整个应用程序中被重复使用。我们所使用的操作如下:

Protected _cn As SqlConnection = Nothing

...

Protected Sub Open()
    If _cn Is Nothing Then
        _cn = New SqlConnection(_sqlConn)
    End If

    If _cn.State = ConnectionState.Closed OrElse _cn.State = ConnectionState.Broken Then
        _cn.Open()
    End If
End Sub

程序在正常执行期间完美运行。但是,应用程序的某些部分以多线程方式执行时,如果进行其他操作,就会频繁出现错误。

经过一番探索,我发现这是因为有时两个不同的线程都试图使用相同的 SqlConnection 对象。

所以,在确定问题后,我现在需要找到解决方案。显而易见的解决方案是每次数据库调用需要连接对象时重新创建 SqlConnection 对象——在这种情况下,它永远不会被共享。是否存在任何理由这样做呢?最初我认为我们仅为了性能原因而对每个应用程序会话使用一个连接对象,但实际上情况如何呢?

如果我们确实需要保持一个连接对象处于打开状态,那么建议的解决方案是什么?我应该设置某种定时器,直到连接对象可用,然后再访问它吗?


如果一个线程的性质是它要对数据库进行多次调用,然后线程关闭,您可能希望在线程中保持连接打开,并随线程一起关闭它。当然,不要跨线程共享连接。 - paparazzo
2个回答

31
明显的解决方案是每次需要数据库调用时重新创建SqlConnection对象 - 在这种情况下,它永远不会被共享。有没有任何理由不这样做呢?
相反,那绝对是你应该做的。这就是SqlConnection设计的行为。您应该使用Using语句在使用它的块结束时自动关闭连接,连接池机制将自动处理与数据库的真实底层连接。

我在想,如果你需要两个线程的更改在同一个事务中进行,那该怎么办呢?难道你不必要“共享”连接吗? - Moslem Ben Dhaou
7
如果你有两个线程想要在同一个事务中做某事,我会认为你已经有了很大的问题。你必须要协调它们之间的操作——否则你怎么知道什么时候提交呢?此时将操作放入生产者/消费者队列中,在一个线程中完成整个事务可能会更好。 - Jon Skeet
我有一个文件共享爬虫(获取权限并在稍后进行审核)。目前它正在启动多个线程来爬取同一文件夹(以加快进程)。我想把每个文件夹都提交到同一个事务中,而不是出现不完整的扫描(当前情况下,当某些线程失败时)。目前没有实现任何事务概念,但我正在评估各种选择。生产者/消费者队列可能是一种选择,但不幸的是内存是限制因素。我想我陷入了一个内存/时间的妥协。还有其他建议吗? - Moslem Ben Dhaou
好的,Jon。几乎每个我问过的人都和你有着相同的回答。所以,我会去做出改变。谢谢! - Kiran Ramaswamy
1
在我的情况下,实施了Jon的方法后,我的应用程序从大约四分之一的线程经历超时并且每个周期需要大约1分钟的时间,变得非常顺畅,并且只需一两秒钟就能完成所有操作。感谢Jon! - Mike
显示剩余2条评论

5

我认为每次需要时创建一个SQL连接是很有必要的。实际上,这可能是最好的方法,因为它让.NET框架能够最有效地管理和重用连接。将每个SQL连接都包装在USING中,以便尽可能短地保留它们。

我们已经创建了一个创建连接并供大家使用的方法:

using (var conn = GetConnection())
    using (var proc = GetProcedure(conn, "procname"))
        using (var reader = proc.GetReader())
        {
            ... DB stuff
        }

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