同步BackgroundWorker

问题描述 投票:0回答:1

我使用BackgroundWorker 每30 秒更新一次表单上的控件。 BackgroundWorker 打开 COM 端口并从串行仪器读取数据。 它断言 ReportProgress() 来更新表单上的控件。 我的表单还包含一个切换开关,可打开与BackgroundWorker 相同的COM 端口以更改仪器的状态。 当切换开关的 StateChanged 事件触发时,我需要停止 BackgroundWorker 的运行,以避免竞争条件并将对 COM 端口的访问限制为一个线程。

以下是我的代码摘录:

       private void BackgroundWorkerStatus_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                while (!BackgroundWorkerStatus.CancellationPending)
                {
                    // Get new values (Open COM Port, read state)
                    ReadState();

                    // Update the User Interface
                    BackgroundWorkerStatus.ReportProgress(0);

                    // Wait before updating again
                    Thread.Sleep(UpdateInterval);
                }
            }
            catch
            {
                MessageBox.Show(….)
            }
        }


        private void BackgroundWorkerStatus_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            try
            {
                 // Update controls on the Form
            }
            catch
            {
                MessageBox.Show(….);
            }


        private void ToggleSwitch_StateChanged(object sender, NationalInstruments.UI.ActionEventArgs e)
        {
                // Open COM Port, set state, update Form
        }

我尝试在 StateChanged 事件开始时调用 CancelAsync() 并在结束时调用 RunWorkerAsync() 。

我尝试创建一个在 StateChanged 事件中设置并在 DoWork 方法中求值的全局布尔值。

这些都不起作用,大概是因为 CancelPending 和全局布尔值不是连续计算的,并且它们的状态在BackgroundWorker线程运行期间发生变化。

我也尝试过使用信号量,但这会在后台线程尝试更新 UI 时导致跨线程异常。 我明白为什么,但不知道如何解决。

本质上我需要 Semaphore 和 BackgroundWorker 的组合来同步后台任务,同时也可以更新 UI。

执行此操作的适当方法是什么?

谢谢!

c# winforms backgroundworker semaphore
1个回答
0
投票

你是对的,直接修改标志或使用来自不同线程的信号量可能会导致竞争条件。以下是如何有效同步您的BackgroundWorker和切换开关:

1。使用手动重置事件:

此事件允许您向BackgroundWorker 线程发出信号以正常停止。

  • 在您的表格课程中:

声明一个名为

ManualResetEvent
的私有
stopEvent
变量。

BackgroundWorkerStatus_DoWork

private void BackgroundWorkerStatus_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        while (!stopEvent.WaitOne(UpdateInterval)) // Wait for either UpdateInterval or stop signal
        {
            if (BackgroundWorkerStatus.CancellationPending)
            {
                break;
            }
            // Get new values (Open COM Port, read state)
            ReadState();

            // Update the User Interface (use Invoke for thread-safe access)
            this.Invoke(new Action(() => UpdateControls()));

            // Wait before updating again
        }
    }
    catch
    {
        MessageBox.Show(….)
    }
}

我们使用带超时功能的

WaitOne
来检查
stopEvent
是否发出信号或
CancellationPending
标志变为 true。这样可以在翻转切换开关时正常关闭。

我们使用

Invoke
从后台线程安全地更新 UI 控件。

  • ToggleSwitch_StateChanged

    private void ToggleSwitch_StateChanged(对象发送者,NationalInstruments.UI.ActionEventArgs e) { 如果(切换开关。选中) { // 打开 COM 端口,设置状态(此处为您的代码) // 更新表单(使用 Invoke 进行线程安全访问) this.Invoke(new Action(() => UpdateForm())); } 别的 { stopEvent.Set(); // 通知BackgroundWorker线程停止 } }

如果切换开关被选中(意味着通信已启用),请照常执行 COM 端口操作并更新表单。

如果未选中切换开关(通信已禁用),请设置

stopEvent
以指示 BackgroundWorker 线程停止。

2。考虑锁定机制(可选):

如果您需要确保从BackgroundWorker线程或切换开关事件处理程序对COM端口的独占访问,您可以使用

Mutex
进行互斥。但是,请谨慎使用,因为过度锁定会影响性能。

3.记得清理:

  • 在表单的 dispose 方法中,调用
    stopEvent.Close()
    来释放 事件句柄。

此方法允许在翻转切换开关时彻底关闭

BackgroundWorker
线程,并确保与 COM 端口和 UI 更新的线程安全通信。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.