2010-04-07 227 views
2

我實現了一個簡單的C#應用​​程序,它將約350000條記錄插入到數據庫中。這用於很好地工作,並且這個過程大約需要20分鐘。C#應用程序過程在一段時間後掛起

我創建了一個進度條,可以讓您大致瞭解記錄插入的進度。當進度條達到75%時,它會停止進度。由於過程似乎並未完成,我必須手動終止程序。如果我使用較少的數據(如10000),則進度條結束並且過程完成。但是,當我嘗試插入所有記錄時,這不會再發生。

請注意,如果我等待更長時間手動終止程序,則會插入更多記錄。例如,如果我在15分鐘後終止程序,將插入200000條記錄,而如果我在20分鐘後終止程序,則會插入250000條記錄。

此程序使用單個線程。面對過程完成之前,我無法做任何其他事情。這與線程或進程有什麼關係?

任何反饋將不勝感激。

謝謝。

+1

這聽起來像是你的代碼有問題,就像是一個異常被無聲地吞噬或類似的東西。你介意與我們分享代碼嗎? – Bobby 2010-04-07 12:08:07

+0

任何反饋都將不勝感激...至少對於其他面臨類似問題的人來說,如果不是因爲那些花時間提供建議的人的好奇心。 – 2010-04-08 09:13:13

回答

9

令人驚訝的是,您的進度條完全可以工作。如果您不使用單獨的線程,那麼長時間運行的任務將阻止消息循環運行,從而導致應用程序無響應。

您應該使用BackgroundWorker運行此任務。將您長時間運行的代碼放入DoWork事件的處理程序中。使用ReportProgess更新進度欄。不要直接從DoWork處理程序中訪問表單控件。

有一些如何在MSDN上執行它的例子。

此外,請確保您不更新每個更改的進度欄。例如,如果您有100,000條記錄,則僅更新每100或1000條記錄的進度欄。太多事件也可能導致程序停止響應。

+1

該應用程序是單線程的,該問題究竟如何與多線程問題相關? – 2010-04-07 12:11:54

+1

@Jorge,如果應用程序在單個線程上,則插入代碼將咀嚼所有資源。所以回調到UI將受到限制(如果有的話) – James 2010-04-07 12:13:05

+0

@Jorge:是的,他的應用程序是單線程的,這就解釋了爲什麼進度條不起作用。我已經更新了答案,以便更清楚地解釋這一點。 – 2010-04-07 12:13:12

0

您在插入過程中如何處理異常?

你插入了什麼樣的數據?它會產生一個異常嗎?

0

您應該使用取消操作(而不是強制關閉)選項在單獨的線程上運行插入。正如@Mark使用類似於BackgroundWorker的建議或者只是創建一個單獨的線程並記下它。看起來好像這個過程在某個時候是瓶頸的,你應該看看做一些日誌記錄。

0

與進度無關,但您是否批量插入?這可能會加快這一過程(並減少資源消耗)。

0

事實:

  • 你的國家,它掛起,而之後的幾行你的狀態,應用程序仍在處理一些東西,後來終止它,它被殺害之前,更多的項目實際處理。

不容易告訴沒有任何源代碼問題是什麼,但我會懷疑由於內存泄漏或其他性能下降因素導致的緩慢下降。

一些猜測:

  • 你關閉/處置所有不再需要的數據庫連接?未配置的數據庫連接可能會造成巨大的內存泄漏並掛起應用程序。

  • 您是否嘗試過在內存/性能分析器中運行應用程序? (螞蟻很棒)

  • 你有沒有試過在一段時間後將調試器附加到應用程序,看看它究竟在哪裏掛起,它是否掛起?

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

如果插入了很多紀錄,儘量使用批量複製。它將顯着提高您的應用程序的速度。 這些函數非常簡單,你把所有的記錄都插入到一個datable中(與目標表具有相同的模式)並用它調用函數。

要抓取數據表模式,如果你懶惰只是做一個像「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); 
      } 
     } 
    } 

至於爲什麼它會減慢,這很簡單,它需要一個查詢的時間與記錄的數量成倍增加執行。因爲它將數據庫的未來狀態存儲在內存中,並且僅在提交後(在您的情況下爲事務結束時)將其寫入,因此請使用批量複製來提交更多提交。

+0

你怎麼設置更多的提交? – seedg 2010-04-07 13:36:00

+0

而是有一個巨大的SQL查詢的,它在批次拆分,我的試驗和錯誤試驗均約500插入批次都是好的。 SQLEXEC (myquery1); SqlExec(myquery2);等等等等 – Jipy 2010-04-07 14:20:44

+0

是不是關於一個MySQL數據庫? – 2010-04-07 14:24:38

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實現該接口,因此是意識到這一點,並能顯示whaever需要它(在我的情況2進度條) 僅臨屋的缺點是,frmLoading必須在構造函數中有這樣的:

Control.CheckForIllegalCrossThreadCalls= false; 

這可能是危險的在某些情況下(不是我的情況)。

希望這有助於我可以添加更多的東西,如果你喜歡。

Regards,

0

畢竟沒有問題。問題是我在使用虛擬機,因此速度有點慢。當我在Xeon服務器上運行它時,該過程在大約10分鐘內完成。

+0

我仍然會按照Mark Byers對BackgroundWorker的建議。使用GUI代碼進行長時間運行的任務=一個非常糟糕的想法。此外,在這種情況下涉及的額外努力將是最小的。 – n4rzul 2010-11-23 10:50:03

相關問題