C# 非常慢的foreach循环

C# 非常慢的foreach循环,c#,.net,ado.net,C#,.net,Ado.net,我正在处理一个现有的应用程序。这个应用程序从一个大文件中读取数据,然后在进行一些计算后,将数据存储到另一个表中 但是这样做的循环(见下文)需要很长时间。由于文件有时包含1000条记录,整个过程需要几天时间 我可以用其他东西替换这个foreach循环吗?我试着使用Parallel.ForEach,它确实有帮助。我是新来的,所以非常感谢你的帮助 foreach (record someredord Somereport.r) { try { using (var co

我正在处理一个现有的应用程序。这个应用程序从一个大文件中读取数据,然后在进行一些计算后,将数据存储到另一个表中

但是这样做的循环(见下文)需要很长时间。由于文件有时包含1000条记录,整个过程需要几天时间

我可以用其他东西替换这个
foreach
循环吗?我试着使用
Parallel.ForEach
,它确实有帮助。我是新来的,所以非常感谢你的帮助

foreach (record someredord Somereport.r)
{
    try
    {
        using (var command = new SqlCommand("[procname]", sqlConn))
        {
            command.CommandTimeout = 0;
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(…);

            IAsyncResult result = command.BeginExecuteReader();
            while (!result.IsCompleted)
            {
                System.Threading.Thread.Sleep(10);
            }
            command.EndExecuteReader(result);
        }
    }
    catch (Exception e)
    {
        …
    }
}
在查看了答案之后,我删除了Async,并对代码进行了如下编辑。但这并没有提高性能

using (command = new SqlCommand("[sp]", sqlConn))
{
    command.CommandTimeout = 0;
    command.CommandType = CommandType.StoredProcedure;
    foreach (record someRecord in someReport.)
    {
        command.Parameters.Clear();
        command.Parameters.Add(....)
        command.Prepare();                            

        using (dr = command.ExecuteReader())
        {
            while (dr.Read())
            {
                if ()
                {

                }
                else if ()
                {

                }
            }
        }                             
    }                        
}

<>而不是多次循环SQL连接,考虑从SQL Server中提取整个数据集,并通过DataSet?< /P>处理数据。 编辑:决定进一步解释我的意思。。 您可以执行以下操作,伪代码如下

  • 使用select*从数据库中获取所有信息,并将它们存储到类或字典的列表中
  • 执行foreach(在someReport中记录someRecord)并像往常一样进行条件匹配

  • 第1步:放弃异步尝试。它没有正确地实现,并且您正在阻塞。所以只要执行这个过程,看看是否有帮助

    步骤2:将SqlCommand移到循环之外,并在每次迭代中重用它。这样,您就不会为循环中的每一项都产生创建和销毁它的成本


    警告:确保重置/清除/删除上一次迭代中不需要的参数。我们使用可选参数做了类似的事情,并且在上一次迭代中使用了“出血通过”,因为我们没有清理不需要的参数

    你最大的问题是你在循环这个问题:

    IAsyncResult result = command.BeginExecuteReader();
    
    while (!result.IsCompleted)
    {
       System.Threading.Thread.Sleep(10);
    }
    
    command.EndExecuteReader(result);
    
    异步模型的整体思想是,调用线程(执行此循环的线程)应该在开始使用End方法处理结果之前,使用Begin方法启动所有异步任务。如果您在主调用线程中使用Thread.Sleep()来等待异步操作完成(正如您在这里所做的那样),那么您的操作是错误的,结果是每次调用一个命令,然后等待下一个命令启动

    相反,请尝试以下方法:

    public void BeginExecutingCommands(Report someReport)
    {
        foreach (record someRecord in someReport.r) 
        {
            var command = new SqlCommand("[procname]", sqlConn);
    
            command.CommandTimeout = 0;
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add(…);
    
            command.BeginExecuteReader(ReaderExecuted, 
                new object[] { command, someReport, someRecord });                   
        }
    }
    
    void ReaderExecuted(IAsyncResult result)
    {
        var state = (object[])result.AsyncState;
        var command = state[0] as SqlCommand;
        var someReport = state[1] as Report;
        var someRecord = state[2] as Record;
    
        try
        {
            using (SqlDataReader reader = command.EndExecuteReader(result))
            {
                // work with reader, command, someReport and someRecord to do what you need.
            }
        }
        catch (Exception ex)
        {
            // handle exceptions that occurred during the async operation here
        }
    }
    

    执行
    SQL
    命令似乎会锁定一些所需的资源,这就是强制您使用
    Async
    方法的原因(我猜)


    如果数据库未在使用中,请尝试以独占方式访问它。即使是由于数据模型的复杂性,也有一些内部事务考虑到数据库设计器。在写的另一端SQL中的

    < P>是一个磁盘。你很少能并行地写得更快。事实上,在并行计算中,由于索引碎片,它的速度往往会减慢。如果可以在加载之前按主键(群集)对数据进行排序。在大负载情况下,甚至禁用其他键,加载数据重建键

    不太确定异步中正在做什么,但可以肯定的是,它没有做您期望的事情,因为它正在等待自己

    try
    {
        using (var command = new SqlCommand("[procname]", sqlConn))
        {
            command.CommandTimeout = 0;
            command.CommandType = CommandType.StoredProcedure;
    
            foreach (record someredord Somereport.r)
            {
                command.Parameters.Clear()
                command.Parameters.Add(…);
    
                using (var rdr = command.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        …
                    }
                }
            }
        }
    }
    catch (…)
    {
        …
    }
    

    正如我们在评论中所说的,将这些数据存储在内存中并使用它可能是一种更有效的方法

    所以一个简单的方法是从实体框架开始。实体框架将根据数据库模式自动为您生成类。然后可以选择保存SELECT语句的。我建议将存储过程导入EF的原因是,这种方法通常比使用LINQ对EF进行查询更有效

    然后运行存储过程并将数据存储在
    列表中,如下所示

    var data=db.MyStoredProc().ToList()

    然后,您可以使用该
    数据执行任何操作。或者正如我提到的,如果你在主键上做了大量的查找,那么使用类似这样的东西

    var data=db.MyStoredProc().ToDictionary(k=>k.MyPrimaryKey)


    无论哪种方式,此时您都将使用内存中的
    数据。

    有两种想法-首先,您的异步操作错误,因此您可能会在循环中的许多项上睡着。第二,你能在整个循环中重复使用SqlCommand对象而不是每次创建/销毁一个吗?如果你告诉我们更多关于你试图实现的目标,我们可能会向你展示一个SQL解决方案,它运行速度快几个数量级,并且完全避免了整个异步/并行业务。@user1110790:您发布的代码充满了错误(并且仍然至少有一个),所以我已经清理了一点。请允许我谦恭地建议,当您发布SO时,请确保您的代码是正确的;除此之外,你可能会得到很多关于这方面的评论,而不是关于实际问题。离题:虽然我同意这里的其他人的观点,你使用异步方法的方式是错误的,但我要补充一点,你永远不应该像这样轮询
    AsyncResult
    While(!result.IsCompleted)Thread.Sleep(…)。相反,您应该这样做:
    result.AsyncWaitHandle.WaitOne()虽然这也会阻止调用线程,但它不需要任何轮询;操作系统将在完成后唤醒调用线程。@RobertHarvey,所以这实际上是服务的一部分,它读取包含帐户信息及其在线使用情况的文件。根据这些数据,我们正在计算总使用量,并使用存储过程将这些信息添加到3个表中。存储过程只是更新现有记录或插入新记录。表很小,只包含5列。+1第二步非常重要,很多人都忘记了这一点。“第2步:将SqlCommand移到循环之外…”是主要的改进+1.但最好将数据加载到强类型集合中,然后