【问题标题】:How to pass booleon back from Worker_ProgressChanged to Worker_DoWork如何将布尔值从 Worker ProgressChanged 传递回 Worker DoWork
【发布时间】:2018-10-03 00:09:49
【问题描述】:

我正在使用后台工作程序读取值并将值传递给 Worker_ProgressChanged,以更新 UI。

在 Worker_DoWork 中:

while (agi.DvmReadyToRead)   // wait for digipot to be adjusted before reading in worker
{
    Thread.Sleep(20);
    Application.DoEvents();
    //logS.Debug("Waiting for ready to read in worker");
}
Thread.Sleep(40);  // Give digipot chance to make the change
agi.SendSoftwareTriggerOne();
Thread.Sleep(7);    // Duration for above command to execute
A = agi.ReadOne();
Thread.Sleep(1);    
agi.InitOne();
Thread.Sleep(1);    
sAndH3 = A[0];
worker.ReportProgress(0, new System.Tuple<double>(sAndH3));
agi.DvmReadyToRead = true;

在 Worker_ProgressChanged 中:

while (!agi.DvmReadyToRead)
{
    //logS.Debug("waiting for ready to read in progress");
    Thread.Sleep(0);
    Thread.Sleep(0);
    Thread.Sleep(0);
    Thread.Sleep(0);
    Thread.Sleep(0);
    Application.DoEvents();  // Exception thown here
    Thread.Sleep(1);     // wait for DVM reading
}
agi.DvmReadyToRead = false;

// Then goes on to adjust output voltage up or down

第一次使用效果很好

Application.DoEvents();

然而,在第一次运行之后,此时我得到了一个 stackoverflow。在这里阅读了许多帖子后,DoEvents 并不是我想要实现的最佳方式。 所以我想要一种将布尔值传递回 DoWork 的方法,或者另一种允许工作人员能够读取 agi.DvmReadyToRead 布尔值的方法。

谢谢!

【问题讨论】:

  • 这看起来像是一个非常失败的解决问题的尝试。但我不确定你的整体问题是什么——你目前向我们展示的只是你试图解决它。除了说我确定它不正确之外,我不确定要给出什么建议——因为我什至不确定BackgroundWorker 是否会成为正确解决方案的一部分,但这就是你真正向我们展示的全部内容。
  • @Damien_The_Unbeliever 感谢 cmets。 1)没有ProgressChanged中的DoEvents,DoWork看不到agi.DvmReadyToRead的状态,所以不测量电压并返回给Progress changed。 2)使用 DoEvents 会导致异常。 3)有什么比 DoEvents 更好地“释放”工人。 4)有没有办法将此 agi.DvmReadyToRead 布尔值传递给工作人员而不是全局变量。请问?
  • DoEvents 非常非常邪恶。使用 SOE 破坏程序的简单方法是允许 ProgressChanged 事件处理程序再次运行,甚至在之前的调用完成之前。再次调用 DoEvents,这允许再次调用 ProgressChanged,等等。你必须学会​​在没有它的情况下生活,这是没有必要的。
  • @HansPassant 谢谢,但是我可以用什么来代替 DoEvents 来“释放”工人呢?
  • 我的水晶球很混浊,但它低声说这是一个单线程 COM 组件。它们无法在工作线程上正确运行。一个简单的解决方法是使用 Timer(工具箱中的那个)并轮询组件。一个实用的间隔值是 15,不能比这更快。没关系,您的 Sleep() 已经比这更长了。或者你需要创建一个适合这样一个组件的非工作线程,它必须调用Application.Run()。一个例子is here。先去找计时器。

标签: c# .net backgroundworker doevents


【解决方案1】:

如果我理解您的问题,您描述的是测试和测量中的一个非常常见的模式,您的仪器在触发后需要一些时间才能获得读数。但是您想知道读取发生的时间以便您可以采取一些行动(比如更新 ProgressBar 或 TextBox)并且您希望能够取消工作循环。

当我需要自己做这件事时,我喜欢使用 System.Threading.Tasks 来简化事情。我将在这里发布一个完整的模式,希望您能找到一些有用的东西来解决您遇到的问题。

为了清楚起见,我试图回答您关于“将布尔值传递回 DoWork 的方法...”的问题,方法是说这样做的一种方法是从 Worker_DoWork 中触发一个可以包含布尔值的事件(例如您问的)或加倍(在我的示例中)或您选择的任何其他信息。

祝你好运!

using System;
using System.ComponentModel;    
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace StackOverflow02
{
    public partial class DVMLoopRunner : Form
    {
        public DVMLoopRunner()
        {
            InitializeComponent();
            DVMReadingAvailable += Form1_DVMReadingAvailable;
            ContinueOrCancel += Form1_ContinueOrCancel;
        }

        // See if User has turned off the Run button then cancel worker
        private void Form1_ContinueOrCancel(Object sender, CancelEventArgs e)
        {
            e.Cancel = !checkBoxRunMeterLoop.Checked;
        }

        // The DVM, after being triggered + some delay, has come up with a new reading.
        private void Form1_DVMReadingAvailable(Object sender, DVMReadingAvailableEventArgs e)
        {
            // To update GUI from worker thread requires Invoke to prevent Cross-Thread Exception
            Invoke((MethodInvoker)delegate
            {
                textBox1.Text = e.Reading.ToString("F4");
            });
        }

        // Make our events so that we can be notified of things that occur
        public event CancelEventHandler ContinueOrCancel;                   
        public event DVMReadingAvailableEventHandler DVMReadingAvailable;

        // This is how we will provide info to the GUI about the new reading
        public delegate void DVMReadingAvailableEventHandler(Object sender, DVMReadingAvailableEventArgs e);
        public class DVMReadingAvailableEventArgs : EventArgs
        {
            public readonly double Reading;
            public DVMReadingAvailableEventArgs(double reading)
            {
                Reading = reading;
            }
        }

        // When the User checks the box, Run the worker loop
        private void checkBoxRunMeterLoop_CheckedChanged(Object sender, EventArgs e)
        {
            if(checkBoxRunMeterLoop.Checked)
            {
                Task.Run(() => ReadDVMWorker());
            }
        }

        // Worker Loop
        private void ReadDVMWorker()
        {
            while(true)
            {
                CancelEventArgs e = new CancelEventArgs();
                ContinueOrCancel?.Invoke(this, e);
                if (e.Cancel) return;               // If User has turned off the Run button then stop worker
                ReadDVM();                          // This worker thread will block on this. So trigger, wait, etc.
            }
        }

        // DVM Takes some period of time after trigger
        void ReadDVM()
        {
            Thread.Sleep(1000);
            double newSimulatedReading = 4.5 + Random.NextDouble();
            DVMReadingAvailable?.Invoke(this, new DVMReadingAvailableEventArgs(newSimulatedReading));
        }
        Random Random = new Random();   // Generate random readings for simulation
    }
}

【讨论】:

  • 非常感谢您的回答,您的水晶球一定没有那么浑浊!我已经构建并测试了您的示例,现在我将尝试在我的应用程序中实现它!特别感谢您花时间编写示例程序,我希望将来我的知识/经验能够发展,因此我也可以帮助他人!
  • 很高兴我能帮上忙!如果您在路上遇到任何颠簸,请告诉我。
  • 我现在已经用我的代码将它改编成一个新的类,效果很好!
  • 不错!谢谢你告诉我。
  • 如果您也来过这个地方请查看我最近与仪器相关的线程和轮询答案use ping in infinite loopbackground validation
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 2013-07-29
  • 1970-01-01
  • 2014-01-29
  • 2014-10-14
相关资源
最近更新 更多