为什么TaskScheduler.Current默认的TaskScheduler? [英] Why is TaskScheduler.Current the default TaskScheduler?

查看:884
本文介绍了为什么TaskScheduler.Current默认的TaskScheduler?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任务并行库是伟大的,我用了很多在过去几个月。然而,有一些真正困扰着我:事实上,<一个href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.current.aspx"><$c$c>TaskScheduler.Current是默认的任务调度程序,而不是<一个href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.default.aspx"><$c$c>TaskScheduler.Default.这绝对是不明显处的文件,也不样品第一眼。

The Task Parallel Library is great and I've used it a lot in the past months. However, there's something really bothering me: the fact that TaskScheduler.Current is the default task scheduler, not TaskScheduler.Default. This is absolutely not obvious at first glance in the documentation nor samples.

当前可能会导致因为其行为微妙的错误取决于你是否是内部的另一项任务是不断变化的。不能容易地确定。

Current can lead to subtle bugs since its behavior is changing depending on whether you're inside another task. Which can't be determined easily.

假如我书面方式异步方法库,采用基于事件的标准异步模式信号的原始同步情况下完成,在相同的方式在.NET Framework XxxAsync方法做(如 DownloadFileAsync )。我决定使用任务并行库进行实施,因为它真的很容易实现具有以下code这种行为:

Suppose I am writting a library of asynchronous methods, using the standard async pattern based on events to signal completion on the original synchronisation context, in the exact same way XxxAsync methods do in the .NET Framework (eg DownloadFileAsync). I decide to use the Task Parallel Library for implementation because it's really easy to implement this behavior with the following code:

public class MyLibrary {
    public event EventHandler SomeOperationCompleted;

    private void OnSomeOperationCompleted() {
        var handler = SomeOperationCompleted;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    public void DoSomeOperationAsync() {
                    Task.Factory
                        .StartNew
                         (
                            () => Thread.Sleep(1000) // simulate a long operation
                            , CancellationToken.None
                            , TaskCreationOptions.None
                            , TaskScheduler.Default
                          )
                        .ContinueWith
                           (t => OnSomeOperationCompleted()
                            , TaskScheduler.FromCurrentSynchronizationContext()
                            );
    }
}

到目前为止,一切正常。现在,让我们调用这个库中单击按钮在WPF或WinForms应用程序:

So far, everything works well. Now, let's make a call to this library on a button click in a WPF or WinForms application:

private void Button_OnClick(object sender, EventArgs args) {
    var myLibrary = new MyLibrary();
    myLibrary.SomeOperationCompleted += (s, e) => DoSomethingElse();
    myLibrary.DoSomeOperationAsync();
}

private void DoSomethingElse() {
    ...
    Task.Factory.StartNew(() => Thread.Sleep(5000)/*simulate a long operation*/);
    ...
}

下面,写库调用的人选择了开始一个新的工作在操作完成时。没有什么不寻常。他或她遵循无处不在网上找到的例子,简单地使用 Task.Factory.StartNew 没有指定的TaskScheduler (和有没有简单的过载,在第二个参数中指定它)。当单独叫,但只要在它是由事件引起的 DoSomethingElse 法正常工作,用户界面​​冻结,因为 TaskFactory.Current 将重用同步上下文任务调度,从我的图书馆延续。

Here, the person writing the library call chose to start a new Task when the operation completes. Nothing unusual. He or she follows examples found everywhere on the web and simply use Task.Factory.StartNew without specifying the TaskScheduler (and there is no easy overload to specify it at the second parameter). The DoSomethingElse method works fine when called alone, but as soon at it's invoked by the event, the UI freezes since TaskFactory.Current will reuse the synchronization context task scheduler from my library continuation.

找出这可能需要一些时间,尤其是如果所述第二任务呼叫在一些复杂的调用堆栈掩埋下来。当然,这里的解决方法是简单,一旦你知道如何一切正常:始终指定 TaskScheduler.Default 为你希望要在线程池中运行的任何操作。然而,也许是第二个任务是由另一个外部库开始,不知道这个行为,并天真地使用 StartNew 没有一个具体的调度。我期待这种情况是相当普遍的。

Finding out this could take some time, especially if the second task call is buried down in some complex call stack. Of course, the fix here is simple once you know how everything works: always specify TaskScheduler.Default for any operation you're expecting to be running on the thread pool. However, maybe the second task is started by another external library, not knowing about this behavior and naively using StartNew without a specific scheduler. I'm expecting this case to be quite common.

包装我的脑袋周围后,我无法理解球队的选择写TPL使用 TaskScheduler.Current ,而不是的TaskScheduler中.DEFAULT 作为默认的:

After wrapping my head around it, I can't understand the choice of the team writing the TPL to use TaskScheduler.Current instead of TaskScheduler.Default as the default:

  • 在这不是很明显的是,默认不是默认的!和文档的严重缺乏。
  • 使用当前真正的任务调度程序取决于调用堆栈的!这是很难保持不变有这种行为。
  • 在这繁琐的指定任务调度与 StartNew 因为你必须先指定任务创建选项,并取消标记,导致长,少读行。这可以通过编写一个扩展方法或创建缓解一个 TaskFactory 使用默认
  • 捕获调用堆栈有额外的性能开销。
  • 当我真的想要一个任务依赖于运行任务的另一位家长,我preFER明确指定它来缓解code阅读,而不是依赖于调用堆栈的法宝。
  • It's not obvious at all, Default is not the default! And the documentation is seriously lacking.
  • The real task scheduler used by Current depends of the call stack! It's hard to maintain invariants with this behavior.
  • It's cumbersome to specify the task scheduler with StartNew since you have to specify the task creation options and cancellation token first, leading to long, less readable lines. This can be alleviated by writing an extension method or creating a TaskFactory that uses Default.
  • Capturing the call stack has additional performance costs.
  • When I really want a task to be dependent on another parent running task, I prefer to specify it explicitly to ease code reading rather than rely on call stack magic.

我知道这个问题听起来相当主观的,但我不能找到一个很好的客观理由说明为什么这种行为是因为它。我敢肯定,我失去了一些东西:那就是为什么我转向你。

I know this question may sound quite subjective, but I can't find a good objective argument as to why this behavior is as it. I'm sure I'm missing something here: that's why I'm turning to you.

推荐答案

我认为目前的行为很有道理。如果我创造我自己的任务调度程序,并开始一些任务启动其他任务,我可能希望所有的任务使用我创建的调度。

I think the current behavior makes sense. If I create my own task scheduler, and start some task that starts other tasks, I probably want all the tasks to use the scheduler I created.

我同意这是奇怪的,有时开始从UI线程任务使用默认的调度,有时不是。但我不知道我将如何让这个更好,如果我被设计了。

I agree that it's odd that sometimes starting a task from the UI thread uses the default scheduler and sometimes not. But I don't know how would I make this better if I was designing it.

关于你提到的具体问题:

Regarding your specific problems:

  • 我认为,开始在指定的调度新任务的最简单的方法就是新任务(拉姆达)。开始(调度)。这样做,你必须指定类型参数,如果该任务返回的东西的缺点。 TaskFactory.Create 可以推断出该类型你。
  • 您可以使用 Dispatcher.Invoke()而不是使用 TaskScheduler.FromCurrentSynchronizationContext()
  • I think the easiest way to start a new task on a specified scheduler is new Task(lambda).Start(scheduler). This has the disadvantage that you have to specify type argument if the task returns something. TaskFactory.Create can infer the type for you.
  • You can use Dispatcher.Invoke() instead of using TaskScheduler.FromCurrentSynchronizationContext().

这篇关于为什么TaskScheduler.Current默认的TaskScheduler?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆