简体   繁体   English

如何对异步Windows窗体应用程序进行编程?

[英]How to program asynchronous Windows Forms Applications?

I'm writting a Windows Forms application in C# that performs a lot of long-running procedures. 我正在用C#编写一个Windows Forms应用程序,该应用程序执行许多长时间运行的过程。 I need to program the application so that the GUI doesn't lock. 我需要对应用程序进行编程,以使GUI不会锁定。 What is the best way to program it? 编程的最佳方法是什么?

I know how to use the following: 我知道如何使用以下内容:

  1. BeginInvoke / EndInvoke BeginInvoke / EndInvoke
  2. Calling Application.DoEvents() repeatedly (probably not a good idea) 反复调用Application.DoEvents() (可能不是一个好主意)
  3. BackgroundWorker
  4. etc. 等等

But how to manage GUI state with call backs, etc... is not trivial. 但是,如何通过回调等方式管理GUI状态并非易事。 Are there solutions for this (in the form of patterns or libraries)? 是否有解决方案(以模式或库的形式)?

Using BackgroundWorker is the simplest way to do what you're trying to do. 使用BackgroundWorker是执行您要执行的操作的最简单方法。 BackgroundWorker simplifies the logic of dealing with threads, leaving you with very little code you have to write. BackgroundWorker简化了处理线程的逻辑,从而使您只需要编写很少的代码。 You just have to handle three events ( DoWork , ProgressChanged , and RunWorkerCompleted ) and follow a few principles: 您只需要处理三个事件( DoWorkProgressChangedRunWorkerCompleted ),并遵循一些原则:

  • Don't ever update the UI inside your long-running method. 永远不要在长时间运行的方法中更新UI。 Have it call ReportProgress instead and handle the ProgressChanged event in the UI logic. 让它调用ReportProgress并在UI逻辑中处理ProgressChanged事件。
  • Understand that since the worker isn't running on the UI thread, an exception that your method throws doesn't automatically show up in the UI. 请理解,由于worker不在UI线程上运行,因此方法引发的异常不会自动显示在UI中。 It just gets silently written to the console. 它只是默默地写入控制台。 It's very important to check the Error property of the RunWorkerCompletedEventArgs when the method is done and handle (or throw) the exception. 当方法完成并检查(或引发)异常时,检查RunWorkerCompletedEventArgsError属性非常重要。 If you don't do this, you won't know that your method failed. 如果不这样做,您将不会知道方法失败。
  • If the method can be cancelled, make sure that it periodically checks the CancellationPending property to see if cancellation has been requested. 如果可以取消该方法,请确保它定期检查CancellationPending属性以查看是否已请求取消。 If it has, once it's done handling the cancellation, set the Cancel property on the DoWorkEventArgs object. 如果有,则在完成取消操作后,在DoWorkEventArgs对象上设置Cancel属性。 Be aware that it's possible for CancellationPending to be true and Cancel to be false; 请注意, CancellationPending可能为true, Cancel可能为false。 this happens, for instance, when the user requests cancellation and the method finishes before it has a chance to check CancellationPending . 例如,当用户请求取消并且该方法在有机会检查CancellationPending之前完成时,就会发生这种情况。
  • Correspondingly, check Cancel in your RunWorkerCompleted event handler, so that your UI can provide the proper response to what happened while the worker was running. 相应地,在RunWorkerCompleted事件处理程序中选中“ Cancel ,以便您的UI可以对工作程序运行时发生的情况提供正确的响应。

The examples in the documentation all show the DoWork event handler calling a method of the form. 文档中的所有示例均显示了调用表单方法的DoWork事件处理程序。 This is almost certainly not what you want to do. 几乎可以肯定这不是您想要做的。 Separating business logic from UI is a good idea in general; 通常,将业务逻辑与UI分开是一个好主意。 in background tasks it's essential. 在后台任务中,这是必不可少的。 If your long-running method is a member of a class that doesn't know that the UI even exists, there's no danger that you'll inadvertently have it call a method that updates a status bar or something. 如果您长时间运行的方法是不知道UI甚至不存在的类的成员,那么就不会有您无意间让它调用更新状态栏或其他内容的方法的危险。

The Model-View-Controller pattern separates the state of your UI from the visual aspects of the UI. Model-View-Controller模式将UI的状态与UI的视觉方面分开。 As long as your MVC implementation is thread aware, it should solve the state management issue for you. 只要您的MVC实现是线程感知的,它就可以为您解决状态管理问题。

This is how I handle multi-threaded UI implementations. 这就是我处理多线程UI实现的方式。

EDIT: Here's a good post on selecting an MVC implementation for WinForms projects. 编辑:这是为WinForms项目选择MVC实现的好帖子

It is relatively simple to use the ThreadPool to fire off long running processes from the UI. 使用ThreadPool从UI触发长时间运行的进程相对简单。 If you want feedback you can use some event handlers to fire on certain events from the long running process, then register for them and update the UI as needed. 如果您需要反馈,则可以使用一些事件处理程序在长时间运行的过程中触发某些事件,然后为它们注册并根据需要更新UI。

MVC is good, but you still need to make sure your process is firing off on a thread other than the UI thread. MVC很好,但是您仍然需要确保您的进程在UI线程以外的其他线程上触发。

All your options are syntactic sugar of doing same thing (Asynchronous execution). 您所有的选择都是做同一件事(异步执行)的语法糖。 May be with different levels of controls. 可能具有不同级别的控件。 I would go with BackgroundWorker, because your GUI (handled by the main thread) will always be responsive. 我将使用BackgroundWorker,因为您的GUI(由主线程处理)将始终响应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM