背景工作者和跨线程问题

时间:2011-06-12 03:47:31

标签: c# multithreading backgroundworker

我有一个Winforms应用程序正常工作..使用BackgroundWorkerThread在处理串行数据到设备期间管理GUI可用性。

工作正常。

现在,我正在添加一个新方法,并复制我在其他形式中所做的事情。但我得到一个跨线程异常。

我宣布我的BWT是这样的:

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

然后,我有一个像这样的方法,这背景工作:

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

}

这已经有效了很多年。我不相信错误存在。我想我可能只是在电话中错过了什么?

4 个答案:

答案 0 :(得分:6)

您无法从BackgroundWorker线程更改UI元素。您必须通过调用Invoke()来回复UI线程。

试试这个

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

答案 1 :(得分:0)

问题是您正在从非UI线程更新UI元素:

这些行不应该在DownloadGpsDataFromDevice

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

利用BackgroundWorker run method bw.ReportProgress(0);。更新ProgressChanged处理程序中的UI,该处理程序是专门为此目的而设计的。

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

答案 2 :(得分:0)

多个线程无法或不应该访问某些实例。您有两种方法可以保护数据免受跨线程异常的影响。

当您使用锁从多个线程访问对象时,可以锁定对象:

object locker = new object();

SomeObject MyObject = new SomeObject();

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

您的第二个选择是使用ManualResetEvent锁定线程。它非常简单,您只需从ManualResetEvent调用WaitOne()来锁定您的线程,而另一个线程访问您的“交叉线程”对象。

在您的情况下,您可能希望从backgroundWorker的reportProgress更改UI。 reportProgress将返回初始线程,然后您可以修改UI。

答案 3 :(得分:0)

你确定这是正确的路线吗?我不认为你应该能够更新你工作人员的ui。尝试注释掉gui更新并清理并构建解决方案,以查看日志记录是否真的是问题。要更新ui,请设置WorkerReportsProgress并为其创建事件处理程序以更新ui并报告worker中的进度。