我实现了一个简单的C#应用程序,它将大约350000条记录插入到数据库中。这曾经很好用,这个过程大约需要20分钟。
我创建了一个进度条,让您大致了解记录插入的进度。当进度条达到约75%时,它会停止前进。我必须手动终止程序,因为该过程似乎没有完成。如果我使用较少的数据(如10000),则进度条结束并且过程完成。但是,当我尝试插入所有记录时,这种情况不会再发生了。
请注意,如果我等待更长时间手动终止程序,则会插入更多记录。例如,如果我在15分钟后终止程序,则插入200000条记录,而如果我在20分钟后终止程序,则插入250000条记录。
该程序使用单个线程。在这个过程完成之前,我无法做任何其他事情。这与线程或进程有什么关系吗?
非常感谢任何反馈。
感谢。
答案 0 :(得分:9)
令人惊讶的是,您的进度条可以正常工作。如果您不使用单独的线程,则长时间运行的任务将阻止消息循环运行,从而导致应用程序无响应。
您应该使用BackgroundWorker运行此任务。将长时间运行的代码放在DoWork事件的处理程序中。使用ReportProgess更新进度条。不要直接从DoWork处理程序内部访问表单控件。
有一些如何在MSDN上进行此操作的示例。
此外,请确保不会为每次更改更新进度条。如果您有100,000条记录,则仅更新每100或1000条记录的进度条。太多事件也可能导致程序停止响应。
答案 1 :(得分:2)
如果插入大量记录,请尝试使用批量复制。它将大大提高您的应用程序的速度。 这些函数非常简单,您可以将所有记录插入到数据表中(与目标表具有相同的模式)并使用它调用函数。
要获取数据表模式,如果您只是进行查询,例如“SELECT * FROM tableName WHERE 0 = 1”,则结果集将只包含表名模式。
private static void InsertTable(DataTable dt)
{
dt.AcceptChanges();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["MyDB"].ToString()))
{
//Destination Table is the same as the source.
bulkCopy.DestinationTableName = dt.TableName;
try
{
// Write from the source to the destination.
bulkCopy.BulkCopyTimeout = 600;
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
}
private static void InsertTableWithIdentity(DataTable dt)
{
dt.AcceptChanges();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(System.Configuration.ConfigurationManager.ConnectionStrings["MyDB"].ToString(), SqlBulkCopyOptions.KeepIdentity))
{
//Destination Table is the same as the source.
bulkCopy.DestinationTableName = dt.TableName;
try
{
// Write from the source to the destination.
bulkCopy.BulkCopyTimeout = 600;
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
}
至于它减速的原因,很简单,查询执行所需的时间会随着记录数量呈指数级增长。因为它在内存中存储了数据库的未来状态,并且只在提交之后写入它(在你的情况下是事务结束)所以使用bulkcopy只需要提交更多的提交。
答案 2 :(得分:0)
在插入过程中如何处理异常?
您要插入哪种数据?可能会产生异常吗?
答案 3 :(得分:0)
您应该在单独的线程上运行插入,并选择取消操作(而不是强制关闭)。正如@Mark使用像BackgroundWorker这样的东西建议,或者只是创建一个单独的线程并记下它。似乎这个过程在某些方面存在瓶颈,你或许应该考虑做一些记录。
答案 4 :(得分:0)
与进度事项无关,但您是否提交批量插入?这可能会大大加快这个过程(并减少资源消耗)。
答案 5 :(得分:0)
事实:
没有任何源代码就不容易说出问题所在,但我怀疑由于内存泄漏或其他性能下降因素导致速度变慢。
一些猜测:
您是否正在关闭/处置所有不再需要的数据库连接?未发布的数据库连接可能会造成巨大的内存泄漏并导致应用程序挂起。
您是否尝试在内存/性能分析器中运行该应用程序? (ANTS很棒)
您是否尝试过一段时间后将调试程序附加到应用程序,以查看它究竟挂起的位置以及它是否完全挂起?
答案 6 :(得分:0)
首先,创建一个更改进度条的函数,然后为该函数添加一个委托。创建另一个线程以更新进度条。你完成后应该看起来像这样。
private delegate void UpdateProgressBarDelegate();
private void UpdateProgressBar()
{
if (this.progressBar1.InvokeRequired)
{
this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar));
}
else
{
//code to update progress bar
}
}
如果您需要包含任何参数,您可以这样做:
this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar), param1, param2);
答案 7 :(得分:0)
我使用线程来做这些事情。
我的代码中的某处:
// Definition
private static Thread TH;
...
// When process starts
TH = new Thread(new ThreadStart(Splash_MyCallBack));
TH.Start();
....
// This method starts the form that shows progress and other data
static private void Splash_MyCallBack()
{
frmLoading FL;
FL = new frmLoading();
FL.ShowDialog();
} /* Splash_MyCallBack*/
// Your process calls Splash_Stop when it is done.
static public void Splash_Stop()
{
TH.Abort();
} /* Splash_Stop*/
frmLoading执行可视化的东西,而在后台我有一个非常耗费处理器的任务。 我的进程向接口报告其进度。 frmLoading实现了该接口,因此它可以识别它并且可以显示它是否需要(在我的情况下有2个进度条) 只有问题是,frmLoading必须在构造函数中有这个:
Control.CheckForIllegalCrossThreadCalls= false;
在某些情况下可能存在风险(不是我的情况)。
希望这有帮助,如果你愿意,我可以添加更多东西。
此致
答案 8 :(得分:0)
毕竟没有问题。问题是我使用的是虚拟机,因此速度有点慢。当我在Xeon服务器上运行它时,该过程在大约10分钟内完成。