winforms 如何使与Application共存的BackgroundWorker响应UI线程发送的事件?

dw1jzc5e  于 2023-10-23  发布在  其他
关注(0)|答案(1)|浏览(125)

我正在编写一个WinForms应用程序。我将有2个线程,一个UI线程和操作线程。UI线程产生Control事件,我希望Operation线程使用它们。操作线程是自动化的,并且将在消耗新事件的同时运行用于硬件控制的任务。控制事件包括但不限于启动/停止自动化
我使用BackgroundWorker是因为我的操作线程也将以MVP架构风格更新UI。
我想什么样的例子,简化为简洁。

public class UI : Form
{
    Operation _operation;
    BackgroundWorker _operationBackgroundWorker;
    
    public UI_Load(object sender, EventArgs e)
    {
        _operationBackgroundWorker.RunWorkerAsync();
    }
    
    private void operationBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        _operation.DoWorkAsync(worker);
    }
    
    private void operationBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // Only executed if something fails.
    }
    
    private void btnStart_Click(object sender, EventArgs e)
    {
        /* Much later in the Application lifecycle,
         * somehow trigger an event on operation thread
         * to begin operation.
         */
    }
}

/* The operation thread. This code must run as long as the 
 * application is open, or until a critical error occurs.
 */
public class Operation
{
    public async Task DoWorkAsync(BackgroundWorker worker)
    {
        // Live as long as the application and consume
        // user control events fired by UI thread.
        while (!worker.CancellationPending)
        {
            /* Make thread "sleep" until an event is triggered.
             * It should be possible to fire an event while another
             * operation is in progress such as stop or manual
             * override (a Queue). Operation thread also uses a
             * BackgroundWorker to execute hardware commands
             * to not block Event queue execution.
             */
             
             // Example operation
             foreach (var event in eventQueue)
             {
                switch (event)
                {
                    case Start:
                        // Run concurrently, for loop moves on
                        // to next event.
                        _automationWorker.RunWorkAsync();
                        // Uses Control.BeginInvoke() on controls
                        // to not block itself.
                        ControlView.ChangeControlToPause();
                    case Pause:
                        PauseAutomationWorker();
                        ControlView.ChangeControlToResume();
                    case Stop:
                        StopAutomationWorker();
                        ControlView.ChangeControlToStopped();
                }
             }
        }
    }
}

我以前用async/await编写过我的应用程序,没有单独的Operation线程。这并不起作用,因为它使代码库难以维护,并且(我未能找到错误)有时会使UI挂起或不响应新的控制输入。因此,我正在寻找一个更清洁的替代品。

e5nqia27

e5nqia271#

BackgroundWorker调用ThreadPool上的DoWork事件处理程序。由于您的需求是拥有一个“操作线程”,我假设该线程应该仅专用于此目的,因此您应该使用Thread构造函数。作为将消息从UI线程传递到Operation线程的传送带,您可以使用BlockingCollection<T>。范例:

_queue = new BlockingCollection<Command>();
_operationThread = new Thread(() =>
{
    foreach (Command command in _queue.GetConsumingEnumerable())
    {
        switch (command)
        {
            case Command.Start:
                // Do something
                break;
            case Command.Pause:
                // Do something else
                break;
        }
    }
})
{ IsBackground = true };
_operationThread.Start();

在UI线程中:

private void btnStart_Click(object sender, EventArgs e)
{
    _queue.Add(Command.Start);
}

在以下形式的FormClosingFormClosed事件中:

_queue.CompleteAdding();
_operationThread.Join();

反过来说,即。将消息从操作线程传递回UI线程是另一回事。您不应该直接从Operation线程操作UI控件。您可以使用Progress<T>示例或Control.Invoke/Control.BeginInvoke方法等方法。

相关问题