C#多线程应用程序和SQL连接有帮助

时间:2010-10-24 13:30:50

标签: c# sql multithreading .net-3.5

我需要一些关于我写的应用程序的建议。我遇到的问题是由于我的DAL和我的SQL Server 2008数据库的连接未被关闭,但是我查看了我的代码并且每个连接始终被关闭。

该应用程序是一个多线程应用程序,它检索一组记录,并在处理记录时更新有关它的信息。

以下是流程:

管理员可以设置要运行的线程数以及每个要拉取的线程数。

以下是点击开始后运行的代码:

适配器是我的DAL的抽象,这里是他们的样子:

public class UserDetailsAdapter: IDataAdapter<UserDetails>
{
     private IUserDetailFactory _factory;

        public UserDetailsAdapter()
        {
            _factory = new CampaignFactory();
        }

        public UserDetails FindById(int id){
             return _factory.FindById(id);
        }
}

一旦_factory被调用,它就会处理SQL并立即关闭连接。

螺纹应用程序代码:

private int _recordsPerthread;


private int _threadCount;

    public void RunDetails()
    {
        //create an adapter instance that is an abstration
        //of the data factory layer
        var adapter = new UserDetailsAdapter();

        for (var i = 1; i <= _threadCount; i++)
        {
            //This adater makes a call tot he databse to pull X amount of records and 
            //set a lock filed so the next set of records that are pulled are differnt.
            var details = adapter.FindTopDetailsInQueue(_recordsPerthread);
            if (details != null)
            {
                var parameters = new ArrayList {i, details};
                ThreadPool.QueueUserWorkItem(ThreadWorker, parameters);
            }
            else
            {
                break;
            }
        }
    }

    private void ThreadWorker(object parametersList)
    {
        var parms = (ArrayList) parametersList;
        var threadCount = (int) parms[0];
        var details = (List<UserDetails>) parms[1];
        var adapter = new DetailsAdapter();


        //we keep running until there are no records left inthe Database
        while (!_noRecordsInPool)
        {
            foreach (var detail in details)
            {
                var userAdapter = new UserAdapter();
                var domainAdapter = new DomainAdapter();

                var user = userAdapter.FindById(detail.UserId);
                var domain = domainAdapter.FindById(detail.DomainId);

                //...do some work here......

                adapter.Update(detail);
            }

            if (!_noRecordsInPool)
            {
                details = adapter.FindTopDetailsInQueue(_recordsPerthread);


                if (details == null || details.Count <= 0)
                {
                    _noRecordsInPool = true;
                    break;
                }
            }
        }
    }

应用程序崩溃,因为数据库似乎存在连接问题。查看DAL的日志文件,我看到了:

  

超时已过期。超时期限   在获得之前经过了   从游泳池连接。这可能   已经发生,因为所有汇集   连接正在使用和最大池   达到了规模

当我在一个线程中运行它时它工作正常。我猜测当我在多个线程中欺骗他时,我显然与DB有太多连接。关于我如何在多个线程中保持运行并确保数据库不会给我任何错误的任何想法。

更新: 我在想我的问题可能是我的数据库中的死锁。以下是我遇到死锁错误时运行的SQL代码:

WITH cte AS ( 
  SELECT TOP (@topCount) *
  FROM
  dbo.UserDetails WITH (READPAST) 
WHERE
  dbo.UserDetails where IsLocked = 0)

UPDATE cte 
  SET 
  IsLocked = 1

  OUTPUT INSERTED.*;

我之前从未遇到过此代码的问题(在其他应用程序中)。我重新组织了我的索引,因为它们是99%的碎片。这没有用。我在这里不知所措。

3 个答案:

答案 0 :(得分:3)

我对代码连接中的哪个位置打开感到困惑,但您可能希望数据适配器实现IDispose(确保在离开using范围时关闭池连接)并将代码包装起来using阻止:

using (adapter = new UserDetailsAdapter())
{
    for (var i = 1; i <= _threadCount; i++)
    {
        [..]
    }
} // adapter leaves scope here; connection is implicitly marked as no longer necessary

ADO.NET使用连接池,因此不需要(并且可能适得其反)显式打开和关闭连接。

答案 1 :(得分:0)

我不清楚你是如何实际连接到数据库的。适配器必须引用连接。

您如何实际初始化该连接?

如果为每个线程使用新适配器,则必须为每个适配器使用新连接。

我对你的环境不太熟悉,但我确信在你的数据库开始抱怨之前你真的需要很多开放的连接!

答案 2 :(得分:0)

嗯,经过一些研究后,我发现SQL Server 2008中可能存在一个错误并运行并行查询。我将不得不挖掘我发现讨论的链接,但我最终在我的服务器上运行了这个:

sp_configure 'max degree of parallelism', 1; GO RECONFIGURE WITH OVERRIDE; GO

这可能会降低整体服务器性能,因此对某些人来说可能不是一种选择,但它对我来说非常有用。

对于某些查询,我添加了MAXDOP(n)(n是要使用的处理器数量)选项,以便它们可以更有效地运行。它确实有所帮助。 其次,我发现我的DAL的Dispose方法正在使用GC.Suppressfinalize方法。所以,我最后的部分没有正确地在我的DAL中射击而没有关闭我的连接。 感谢所有提出意见的人!