我正在开发一个 Windows 窗体应用程序,它在指定的时间间隔后调用 WCF 服务,并根据从服务接收到的数据显示输出。我计划为此使用计时器,在 500 毫秒后调用该 WCF 服务方法。但是我的一些同事告诉我使用后台工作人员,然后在Work_Completed
事件中重新运行工作人员。我想知道这两者有什么区别?定时器是否也创建一个后台线程?哪一个最适合长时间运行的任务?
4 回答
就资源消耗而言, ATimer
几乎可以肯定更合适。ABackgroundWorker
将为该任务创建一个新线程。创建一个新线程是一项相当昂贵的操作。虽然有许多不同的计时器实现,并且它们的实现会有所不同,但它们通常会依赖于定期触发事件的操作系统工具,这比启动新的专用线程要好。
对象的大多数主要区别在于Timer
它们“准备好”时所做的事情。有的创建一个新的线程池线程;有些有一个专用线程,由计时器的所有实例共享以运行处理程序,有些将代码编组到 UI 线程(或其他一些同步上下文),您可能想要后者。如果您使用在您正在使用的特定 UI 框架中提供的计时器,您将看到它的行为。
BackgroundWorker 在 ThreadPool 上创建一个线程(通过异步委托调用)。BCL 计时器使用 ThreadPool 线程来引发事件,或者在某些情况下会引发 UI 线程上的滴答/经过/等事件(在 WinForms 计时器或提供给它的 ISynchronizeInvoke 的 Timers.Timer 的情况下)。
BackgroundWorker 为 UI 环境中的后台线程提供了最佳 API。它允许方法在后台线程上运行(通过 DoWork 事件),并提供一种简单的方法来通知 UI 线程上的进度和完成(ProgressChanged 和 RunWorkerCOmpleted 事件),而无需担心任何跨线程调用。
WinForms 计时器在 UI 线程上执行,并且可以安全地从其经过的事件中触摸任何 UI 元素。但是,您不能保证以精确的时间间隔执行,这完全取决于 UI 线程上发生的事情。让我重复一件重要的事情:这个选项没有后台线程!
Threading.Timer 是一种“丑陋的 API”计时器。Timers.Timer 简单地将 Threading.Timer 包装到一个更好的 API 中,并提供了一种在 UI 线程上自动调用 elapsed 事件的方法(通过 SynchronizingObject 属性)。如果您设置 SynchronizingObject 属性,则不会在后台线程上执行任何代码,它会立即编组到 UI 线程。
一般来说,在 UI 中,BackgroundWorker 比其他选项更容易处理。
以我的经验, Background Worker的主要好处是ProgressChanged
andRunWorkerCompleted
事件将在实例化 Worker 的同一线程上执行。因此,如果您在 UI 线程上启动后台工作程序,则 RunWorkerCompleted 事件也会在 UI 线程上触发。这样一来,如果您想在后台工作完成时更新 UI 组件,您就不必担心进行非法的跨线程调用。
A standard System.Windows.Forms.Timer
should be fine. It calls its callback on the UI thread which means that while you are processing inside the callback it will potentially freeze your UI responsiveness. But that's only an issue if you do a lot of work inside the callback or if you block the thread.
To avoid blocking the thread, just execute an asynchronous WCF call.
A BackgroundWorker
makes no sense for what you want to do. All you want to do is to wait 500ms before executing some code so just use a System.Windows.Forms.Timer
.