C# 非常慢的foreach循环
我正在处理一个现有的应用程序。这个应用程序从一个大文件中读取数据,然后在进行一些计算后,将数据存储到另一个表中 但是这样做的循环(见下文)需要很长时间。由于文件有时包含1000条记录,整个过程需要几天时间 我可以用其他东西替换这个C# 非常慢的foreach循环,c#,.net,ado.net,C#,.net,Ado.net,我正在处理一个现有的应用程序。这个应用程序从一个大文件中读取数据,然后在进行一些计算后,将数据存储到另一个表中 但是这样做的循环(见下文)需要很长时间。由于文件有时包含1000条记录,整个过程需要几天时间 我可以用其他东西替换这个foreach循环吗?我试着使用Parallel.ForEach,它确实有帮助。我是新来的,所以非常感谢你的帮助 foreach (record someredord Somereport.r) { try { using (var co
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>处理数据。 编辑:决定进一步解释我的意思。。 您可以执行以下操作,伪代码如下
第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.但最好将数据加载到强类型集合中,然后