C# 应用程序进程在一段时间后挂起

发布于 2024-08-28 03:52:22 字数 405 浏览 7 评论 0原文

我实现了一个简单的 C# 应用程序,它将大约 350000 条记录插入数据库。过去效果很好,整个过程大约需要 20 分钟。

我创建了一个进度条,可以让您了解记录插入的大致进度。当进度条达到 75% 左右时,它会停止进度。我必须手动终止该程序,因为该过程似乎没有完成。如果我使用较少的数据(例如 10000),进度条就会结束,该过程就会完成。但是,当我尝试插入所有记录时,这种情况不会再发生了。

请注意,如果我等待更长的时间来手动终止程序,则会插入更多记录。例如,如果我在 15 分钟后终止程序,则会插入 200000 条记录,而如果我在 20 分钟后终止程序,则会插入 250000 条记录。

该程序使用单线程。表面上,在该过程完成之前我无法做任何其他事情。这与线程或进程有什么关系吗?

任何反馈将不胜感激。

谢谢。

I implemented a simple C# application which inserts about 350000 records into the database. This used to work well and the process took approximately 20 minutes.

I created a progress bar which lets you know approximately the progress of the records insertion. When the progress bar reaches about 75% it stops progressing. I have to manually terminate the program as the process doesn't seem to complete. If I use less data (like 10000), the progress bar finishes and the process is completed. However when I try to insert all the records, this won't happen any more.

Note that if I wait longer to terminate the program manually, more records would have been inserted. For example, if I terminate the program after 15 minutes, 200000 records are inserted, whereas if I terminate the program after 20 minutes, 250000 records are inserted.

This program is using a single thread. In face I can't do anything else until the process is complete. Does this have anything to do with threading or processes?

Any feedback will be greatly appreciated.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

被翻牌 2024-09-04 03:52:23

首先,创建一个更改进度栏的函数,然后为该函数添加一个委托。创建另一个线程来更新进度条。完成后它应该看起来像这样。

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);

First, create a function that changes the Progress Bar, then add a delegate for that function. Create another thread to update the progress bar. It should look like this when you're done.

private delegate void UpdateProgressBarDelegate();

private void UpdateProgressBar()
{
     if (this.progressBar1.InvokeRequired)
     {
         this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar));
     }
     else
     {
         //code to update progress bar
     }    
}

If you need to include any parameters, you would do so like this:

this.progressBar1.Invoke(new UpdateProgressBarDelegate(UpdateProgressBar), param1, param2);
柒夜笙歌凉 2024-09-04 03:52:23

我使用线程来处理这类事情。

在我的代码中的某处:

// 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;

这在某些情况下可能存在风险(不是我的情况)。

希望这会有所帮助,如果您愿意,我可以添加更多内容。

问候,

I use threads for these kind of stuff.

somewhere in my code:

// 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 performs the visual stuff, while in the background I have a very processor-intensive task.
My process reports to an interface its progress. frmLoading implements that interface so it is aware of it and can show whaever it is needed (2 progress bars in my case)
Tha only catch is, frmLoading must have this in the constructor:

Control.CheckForIllegalCrossThreadCalls= false;

which may be risky in some scenarios (not my case).

Hope this helps, I can add more stuff if you like.

Regards,

川水往事 2024-09-04 03:52:23

毕竟没有什么问题。问题是我使用的是虚拟机,因此速度有点慢。当我在 Xeon 服务器上运行此程序时,该过程在大约 10 分钟内完成。

There wasn't a problem after all. The problem was that I was using a virtual machine and therefore it was a bit slow. When I ran this on a Xeon server, the process got completed in about 10 minutes.

逆流 2024-09-04 03:52:22

令人惊讶的是你的进度条居然还能工作。如果您不使用单独的线程,那么长时间运行的任务将停止消息循环的运行,导致您的应用程序无响应。

您应该使用 BackgroundWorker 运行此任务。将长时间运行的代码放入 DoWork< 的处理程序中/a> 事件。使用 ReportProgess 更新进度栏。不要直接从 DoWork 处理程序内部访问表单控件。

MSDN 上有一些如何执行此操作的示例。

另外,请确保不要为每一个更改都更新进度条。例如,如果您有 100,000 条记录,则仅每 100 或 1000 条记录更新进度条。太多的事件也会导致程序停止响应。

It is surprising that your progress bar works at all. If you don't use a separate thread then your long running task will stop the message loop from running, causing your application to be unresponsive.

You should run this task using a BackgroundWorker. Put your long-running code inside a handler for the DoWork event. Use ReportProgess to update the progress bar. Don't access form controls directly from inside the DoWork handler.

There are some examples of how to do it on MSDN.

Also, make sure that you don't update the progress bar for every single change. If you have 100,000 records, only update the progress bar for every 100 or 1000 records, for example. Too many events can also cause the program to stop responding.

离旧人 2024-09-04 03:52:22

如果插入大量记录,请尝试使用批量复制。它将显着提高您的应用程序的速度。
这些函数非常简单,您将所有要插入的记录放入数据表中(与目标表具有相同的架构)并使用它调用该函数。

如果您懒惰,要获取数据表架构,只需进行“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只需放置更多提交。

If you insert a lot of records, try to use bulk copy. It will dramatically raise the speed of your application.
These functions are quite straightforward, you put all your records to be inserted in a datable (with the same schema as the destination table) and call the function with it.

To grab the datatable schema if you're lazy just make a query like "SELECT * FROM tableName WHERE 0=1", the resultset will only contain the tablename schema.

    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);
            }
        }
    }

As for why it slows down, it's simple, the time it takes for a query to execute increases exponentially with the number of records. Because it stores in memory the future state of the database and only write it after the commit (in your case the end of the transaction) so use bulkcopy of simply put some more commits.

记忆で 2024-09-04 03:52:22

插入过程中如何处理异常?

您要插入什么类型的数据?它会产生异常吗?

How are you treating exceptions during the process of insertion?

What kind of data are you inserting? Could it be generating an exception?

耀眼的星火 2024-09-04 03:52:22

您应该在单独的线程上运行插入,并可以选择取消操作(而不是强制关闭)。正如@Mark建议的那样,使用诸如BackgroundWorker之类的东西,或者只是创建一个单独的线程并记下它。似乎这个过程在某个时刻遇到了瓶颈,您也许应该考虑做一些日志记录。

You should run your insertion on a separate thread with the option to cancel the operation (rather than forcing shutdown). As suggested by @Mark using something like a BackgroundWorker or just create a separate thread and take a note of it. It seems as though the process is bottlenecking at somepoint you should perhaps look at doing some logging.

夜巴黎 2024-09-04 03:52:22

事实:

  • 您声明它挂起几行,然后您声明应用程序仍在处理某些内容,并且您越晚终止它,在它被杀死之前实际处理的项目就越多。

如果没有任何源代码,很难判断问题出在哪里,但我怀疑由于内存泄漏或其他性能下降因素而导致速度减慢。

一些猜测:

  • 您是否正在关闭/处置所有不再需要的数据库连接?未处置的数据库连接可能会造成巨大的内存泄漏并挂起应用程序。

  • 您是否尝试过在内存/性能分析器中运行应用程序? (ANTS 很棒)

  • 您是否尝试在一段时间后将调试器附加到应用程序,以查看它到底挂在哪里以及它是否根本挂起?

Facts:

  • you state that it hangs while a few lines after that you state that the application is still processing something and the later you terminate it, the more items are actually processed before it is killed.

Not easy to tell what the problem is without any source code, but I would suspect a slow down due to a memory leak or other performance degradation factor.

A few guesses:

  • Are you closing/disposing all your no longer needed database connections? Undisposed database connections can create huge memory leaks and hang the application.

  • Have you tried running the application in a memory/performance profiler? (ANTS is great)

  • Have you tried attaching a debugger to the application after some time to see where exactly it hangs and whether it hangs at all?

兰花执着 2024-09-04 03:52:22

与进度无关,但您是否正在提交批量插入?这可能会大大加快该过程(并减少资源消耗)。

Not related to the progress thing, but are you commiting batches of inserts? This might speed up the process quite a lot (and reduce resources consumption).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文