2011-06-12 52 views
3

我有一個Winforms應用程序,它工作正常..使用BackgroundWorkerThread在串行數據處理過程中管理GUI可用性。後臺工作者和跨線程問題

它工作正常。

現在,我添加一個新的方法,並複製我以其他形式做的事情。但我得到一個交叉線程異常。

我宣佈我的BWT這樣的:

BackgroundWorker bw = new BackgroundWorker(); 
      bw.WorkerSupportsCancellation = true; 
      bw.DoWork += DownloadGpsDataFromDevice; 
      bw.WorkerReportsProgress = true; 
      bw.RunWorkerAsync(); 

那麼我delared這樣的方法,它不工作背景:

private void DownloadGpsDataFromDevice(object sender, DoWorkEventArgs e) 
{ 
    _performScreenUpdate = true; 
    tsStatus.Text = "Downloading GPS Data..."; 
    Invalidate(); 
    Refresh(); 


    Common.WriteLog("Extracting raw GPS data. Sending LL."); 

    ReplyString raw = DeviceServices.ExecuteCommand("$LL"); 

的DeviceServices.ExecuteCommand( 「$ LL」) ;是做這項工作的那個位,但是我在前一行發現異常,我登錄到一個文本文件。現在,這讓你擔心 - 寫入文件。但是,在另一個BWT中我已經完成了數千次。

我使寫作線程安全。這裏我的Common.WriteLog方法:

public static void WriteLog(string input) 
{ 
    lock (_lockObject) 
    { 
     WriteLogThreadSafe(input); 
    } 
} 

private static void WriteLogThreadSafe(string input) 
{ 
    Directory.CreateDirectory(LogFilePath); 
    StreamWriter w = File.AppendText(LogFilePath + @"\" + LogFileName); 
    try 
    { 
     w.WriteLine(string.Format("{0}\t{1}", DateTime.Now, input)); 
    } 
    catch (Exception e) 
    { 
     System.Console.WriteLine("Error writing to log file!"); 
     System.Console.WriteLine("Tried to write: [" + input + "]"); 
     System.Console.WriteLine("Failed with error: [" + e.Message + "]"); 
    } 
    finally 
    { 
     w.Close(); 
    } 

} 

這已經工作了很長時間。我不相信錯誤在那裏。我想我可能只是在電話中缺少一些東西?

回答

6

您無法從BackgroundWorker線程更改UI元素。你必須通過調用Invoke()來回到UI線程。

試試這個

private void DownloadGpsDataFromDevice(object sender, DoWorkEventArgs e) 
{ 
    _performScreenUpdate = true; 
    Invoke((MethodInvoker)(() => { 
      tsStatus.Text = "Downloading GPS Data..."; 
      Invalidate(); 
      Refresh(); 
    }); 
    ... 
+0

這些行似乎行得通......或者我得到一個錯誤的錯誤?它在Common.WriteLog(「提取原始GPS數據。發送LL。」)上崩潰; – Craig 2011-06-12 03:51:31

+0

等等......你說得對,看來!該應用程序在LogLine上崩潰,但是當我註釋掉你提到的那些行時......它就會起作用。奇怪的是,它在日誌行上崩潰了。謝謝! – Craig 2011-06-12 03:53:33

+0

@cdotlister'WriteLog()'對我來說似乎沒問題,不應該引起跨線程異常。在這種情況下我不確定。 – 2011-06-12 03:54:16

0

的問題是,要更新來自非UI線程的UI元素:

這些線不應該是DownloadGpsDataFromDevice

tsStatus.Text = "Downloading GPS Data..."; 
Invalidate(); 
Refresh(); 

內要利用BackgroundWorker運行方法bw.ReportProgress(0);。在ProgressChanged處理程序中更新UI,該處理程序專爲此目的而設計。

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    if (e.ProgressPercentage = 0) 
    { 
     tsStatus.Text = "Downloading GPS Data..."; 
     Invalidate(); 
     Refresh(); 
    } 
} 
+0

這些行似乎行得通......或者我得到一個錯誤的錯誤?它在Common.WriteLog(「提取原始GPS數據。發送LL。」)上崩潰; – Craig 2011-06-12 03:51:17

+0

等等......你說得對,看起來好像!該應用程序在LogLine上崩潰,但是當我註釋掉你提到的那些行時......它就會起作用。奇怪的是,它在日誌行上崩潰了。謝謝! – Craig 2011-06-12 03:53:15

+0

@cdotlister - 如果您不使用「ProgressChanged」或「RunWorkerCompleted」事件,則不需要使用。這兩個事件是「BackgroundWorker」的全部要點。這些事件總是在UI線程中觸發。 – 2011-06-12 04:21:09

0

有些實例不能或不應該被多個線程訪問。您有兩種選擇來保護數據免受跨線程異常的影響。

您可以鎖定的對象,當你從多個線程具有鎖訪問:

對象更衣室=新的對象();

SomeObject MyObject = new SomeObject();

private void FromMultipleThread() 
{ 
    lock(locker) 
    { 
     MyObject = OtherObject; 
    } 
} 

你的第二個選擇是用一個ManualResetEvent鎖定你的線程。這很簡單,你只需要調用WaitOne()從你的ManualResetEvent來鎖定你的線程,而其他線程訪問你的「交叉線程」對象。

就你而言,你會想要從你的backgroundWorker的reportProgress中更改你的UI。 reportProgress將返回到最初的線程,然後您可以修改您的UI。

0

你確定這是正確的嗎?我不認爲你應該能夠更新你的工作人員的用戶界面。嘗試註釋gui更新並清理並構建解決方案,以查看日誌記錄是否確實是問題。要更新ui,請設置WorkerReportsProgress併爲其創建一個事件處理程序,以更新ui並報告worker中的進度。