Threading.Tasks.TaskScheduler - 请帮我解决这个问题

发布于 2024-10-14 23:37:21 字数 759 浏览 1 评论 0原文

我正在开发一个应用程序,该应用程序将运行相当长一段时间的后台进程,并且我想向 UI 报告进度。我想使用 .Net 4.0 中的任务,以便对它们有一个有效的理解。我发现 Stephen Cleary 写的一篇精彩文章展示了这一点,并包含一个类 ProgressReporter(文章链接在底部)。

我可以直接上课并按原样使用它,但我并不拥有这些知识。我根本无法理解的部分是关于 TaskScheduler 对象。我已阅读文档,但它假设我已经知道调度程序是什么,但实际上我不知道。

从 Stephen 的文章来看,如果我使用“FromCurrentSynchronizationContext”从即将启动后台任务的事件中创建一个 TaskScheduler,那么我可以在异步进程中使用该 TaskScheduler 实例来创建一个新任务,然后该任务将具有访问 UI 控件,并且实际上将在 UI 线程上运行。

至少从干净、优雅的代码来看是这样的。

有人可以帮助我理解 TaskScheduler (或只是一个 Scheduler)是什么,以便“FromCurrentSynchronizationContext”有意义吗?

非常感谢!

参考文章链接: http://nitoprograms.blogspot.com/2010/06/reporting -progress-from-tasks.html

I am working on an application that will have a background process running for quite a while and I want to report progress to the UI. I want to use Tasks in .Net 4.0 so that I gain a working understanding of them. I found a wonderful article by Stephen Cleary that shows this and includes a class, ProgressReporter (article link at the bottom).

I could just take the class and use it as-is, but then I don't own the knowledge. The part that I simply cannot understand is regarding the TaskScheduler object. I have read the documentation, but it makes the assumption that I already know what a Scheduler is and I don't, actually.

From Stephen's article it appears that if I create a TaskScheduler, using "FromCurrentSynchronizationContext", from the event that's about to kick off the background task, then I can use that TaskScheduler instance from within the async process to create a new task that will then have access to UI controls and will, in fact, be running on the UI thread.

At least that's what it looks like from the clean, elegant code.

Can someone help me understand what a TaskScheduler (or just a Scheduler) is so that the "FromCurrentSynchronizationContext" makes sense?

Thanks so much!

Link to article referenced:
http://nitoprograms.blogspot.com/2010/06/reporting-progress-from-tasks.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

空心↖ 2024-10-21 23:37:21

首先,请了解Task 本身与ThreadPool 没有关系。将 Task 视为一个工作单元会有所帮助,仅此而已。它实际上只是一个花哨的代表。

TaskScheduler 是可以运行Task 的东西。默认的 TaskSchedulerThreadPool 上运行 Task,但这并不是唯一的 TaskScheduler。您可以调用 TaskScheduler.FromCurrentSynchronizationContext 来获取在 UI 线程上运行 TaskTaskScheduler

ParallelExtensionsExtras 库包含几个有趣的 TaskScheduler。有关详细信息,请参阅 此中的帖子 5、6 和 7系列

因此,回到我的 ProgressReporter 类,我正在做的是:

  • ProgressReporter 在 UI 线程上创建,它捕获 TaskScheduler< /code> 将在 UI 线程上运行 Task
  • 然后,您运行一个后台Task(其中“后台”意味着它在ThreadPool 上的默认TaskScheduler 上运行)。
  • 当后台 Task 想要报告进度时,它会创建一个Task 并将其发送到 UI TaskScheduler

我应该指出,异步 CTP 使用略有不同的方法。他们建议需要报告进度的任务采用 IProgress 参数(这是一个好主意)。然后,它们提供一个实现 (EventProgress),该实现在捕获的 SynchronizationContext 上引发事件。使用 EventProgress 与我的 ProgressReporter 类似,但有两个重要区别:

  • IProgress 方法具有消费者 em> 定义对进度更新的响应。我的ProgressReporter 让生产者(后台操作)定义对进度更新的响应。
  • IProgress 将立即返回,允许后台操作继续。 ProgressReporter 默认情况下会等待 UI 更新。

由于第一点,IProgress 是更好的设计(尽管我认为 ProgressReporter 在使进度报告同步方面更正确)。 ProgressReporter 当时还不错,可以快速编写一些代码,但如果您正在设计可重用的后台组件,请使用类似于 IProgress 的东西。

如果您不熟悉 Async CTP,我强烈建议您查看一下。它尚未准备好用于生产,但您至少应该阅读“基于任务的异步模式”论文。它显示了事情的进展情况,并且是一种经过深思熟虑的设计使用 Task 的 API 的方法。

PS 我在上面做了一些简化:

  • 任务实际上只是一个工作单元,甚至不是“花哨的委托”。事实上,工作单元实际上可能是由另一个程序完成的,在这种情况下,根本没有委托附加到 Task 对象。您可以使用 TaskCompletionSource 类创建这些抽象的“工作单元”。
  • 我没有深入探讨 SynchronizationContext 是什么或为什么使用它来安排 UI 的工作。本质上,它是一个“委托调度程序” - 如果您将委托视为“工作单元”,则可以使用 SynchronizationContext 来运行委托。更多信息将在下周的 MSDN 二月号中发布。

First, understand that a Task by itself has no relation to the ThreadPool. It helps to think of a Task as a unit of work, nothing more. It's actually just a fancy delegate.

A TaskScheduler is something that can run a Task. The default TaskScheduler runs Tasks on the ThreadPool, but that's not the only TaskScheduler around. You can call TaskScheduler.FromCurrentSynchronizationContext to get a TaskScheduler that runs Tasks on a UI thread.

The ParallelExtensionsExtras library includes several interesting TaskSchedulers. For details, see posts 5, 6, and 7 in this series.

So, to come back to my ProgressReporter class, what I'm doing is this:

  • The ProgressReporter is created on the UI thread, and it captures the TaskScheduler that will run Tasks on the UI thread.
  • You then run a background Task (where "background" means that it's running on the default TaskScheduler - on the ThreadPool).
  • When the background Task wants to report progress, it creates a new Task and sends it to the UI TaskScheduler.

I should point out that the Async CTP uses a slightly different approach. They recommend that tasks needing to report progress take an IProgress<T> argument (which is a great idea). Then they provide an implementation (EventProgress<T>), which raises an event on a captured SynchronizationContext. Using EventProgress<T> is similar to my ProgressReporter with two important differences:

  • The IProgress<T> approach has the consumer define the response to the progress update. My ProgressReporter has the producer (the background operation) define the response to the progress update.
  • IProgress<T> will return immediately, allowing the background operation to continue. ProgressReporter by default will wait until the UI has been updated.

The IProgress<T> is the better design, because of the first point (though I'm of the opinion that ProgressReporter is more correct in making the progress reporting synchronous). ProgressReporter was fine for the time, and it's OK for banging out some quick code, but if you're designing a reusable background component, use something similar to IProgress<T>.

If you aren't familiar with the Async CTP, I highly recommend checking it out. It's not ready for production use yet, but you should at least read the "Task-based Asynchronous Pattern" paper. It shows where things are going, and is a well-thought-out approach to designing APIs that use Tasks.

P.S. There are a couple of simplifications I made above:

  • Tasks are really just a unit of work, not even a "fancy delegate". In fact, the unit of work may actually be done by another program, in which case there is no delegate attached to the Task object at all. You can create these abstract "units of work" by using the TaskCompletionSource class.
  • I didn't go into what a SynchronizationContext is or why it's used to schedule work to the UI. In essence, it's a "delegate scheduler" - if you consider a delegate as a "unit of work", a SynchronizationContext can be used to run delegates. More information will be in the MSDN February issue next week.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文