后台工作者和跨线程问题

发布于 2024-11-14 19:41:13 字数 1695 浏览 3 评论 0原文

我有一个运行良好的 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();
    }

}

这已经工作了很长时间。我不相信错误在那里。我想我可能只是在通话中漏掉了一些东西?

I have a Winforms application which is working fine.. using a BackgroundWorkerThread to manage GUI usability during processing of serial data to a device.

It's working fine.

Now, I am adding a new method, and copying what I did in other forms. But I am getting a cross thread exception.

I declare my BWT like this:

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

I then have a method delared like this, which does the background work:

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

The DeviceServices.ExecuteCommand("$LL"); is the bit that does the work, but I am getting the exception on the previous line, where I log to a text file. Now, that makes you worry - writing to a file. However, I have done this thousands of times in another BWT.

I made the writing thread safe. Here this my Common.WriteLog method:

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

}

This have been working for ages. I don't believe the error is there. I think I am just missing something on the call maybe?

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

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

发布评论

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

评论(4

一身骄傲 2024-11-21 19:41:13

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

试试这个

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

You cannot change UI elements from BackgroundWorker thread. You'll have to marshall back to UI thread by calling Invoke().

Try this

private void DownloadGpsDataFromDevice(object sender, DoWorkEventArgs e)
{
    _performScreenUpdate = true;
    Invoke((MethodInvoker)(() => {
             tsStatus.Text = "Downloading GPS Data...";
             Invalidate();
             Refresh();
     });
     ...
北城孤痞 2024-11-21 19:41:13

问题是您正在从非 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();
    }
}

The issue is that you are updating UI elements from non-UI thread:

Those lines should not be inside of DownloadGpsDataFromDevice

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

To take advantage of BackgroundWorker run method bw.ReportProgress(0);. Update UI in ProgressChanged handler, which was specifically designed for this purpose.

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    if (e.ProgressPercentage = 0)
    {
        tsStatus.Text = "Downloading GPS Data...";
        Invalidate();
        Refresh();
    }
}
哭泣的笑容 2024-11-21 19:41:13

某些实例不能或不应该由多个线程访问。您有两种选择来保护数据免受跨线程异常的影响。

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

object locker = new object();

SomeObject MyObject = new SomeObject();

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

您的第二个选择是使用 ManualResetEvent 锁定您的线程。这非常简单,您只需从 ManualResetEvent 调用 WaitOne() 即可锁定您的线程,同时其他线程访问您的“跨线程”对象。

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

Some instances can't or should not be accessed by multiple threads. You have two options to protect your data from cross thread exceptions.

You can lock your object when you access it from multiple threads with a lock:

object locker = new object();

SomeObject MyObject = new SomeObject();

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

Your second option is to lock your thread with a ManualResetEvent. It is very simple, you only have to call WaitOne() from you ManualResetEvent to lock your thread while an other thread access your "cross threaded" Object.

In your case, you would want to change your UI from the reportProgress of your backgroundWorker. The reportProgress will come back to the initial thread, then you can modify your UI.

穿越时光隧道 2024-11-21 19:41:13

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

Are you sure that is the right line? I don't think you should be able to update the ui in your worker. Try commenting out the gui update and clean and build your solution to see if the logging is really the problem. To update the ui, set WorkerReportsProgress and create an event handler for that to update the ui and report progress in the worker.

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