2011-06-12 54 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中的进度。