何时使用TaskEx.Run与TaskEx.RunEx

9

我正在尝试理解何时使用 TaskEx.Run。我提供了下面两个代码示例,它们产生相同的结果。但是我没有看到为什么要采用 Task.RunEx TaskEx.RunEx 方法,我相信有一个很好的理由,并希望有人能告诉我。

async Task DoWork(CancellationToken cancelToken, IProgress<string> progress)
{
    int i = 0;
    TaskEx.RunEx(async () =>
        {
            while (!cancelToken.IsCancellationRequested)
            {
                progress.Report(i++.ToString());
                await TaskEx.Delay(1, cancelToken);
            }
        }, cancelToken);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
    if (button.Content.ToString() == "Start")
    {
        button.Content = "Stop";
        cts.Dispose();
        cts = new CancellationTokenSource();
        listBox.Items.Clear();
        IProgress<string> progress = new Progress<string>(s => 
        {
            listBox.Items.Add(s); 
            listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
        });
        DoWork(cts.Token, progress);
    }
    else
    {
        button.Content = "Start";
        cts.Cancel();
    }
}

我可以这样实现相同的结果
  async Task DoWork(CancellationToken cancelToken)
    {
        int i = 0;
        while (!cancelToken.IsCancellationRequested)
        {
            listBox.Items.Add(i++);
            listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
            await TaskEx.Delay(100, cancelToken);

        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (button.Content.ToString() == "Start")
        {
            button.Content = "Stop";
            cts.Dispose();
            cts = new CancellationTokenSource();
            listBox.Items.Clear();
            DoWork(cts.Token);
        }
        else
        {
            button.Content = "Start";
            cts.Cancel();
        }
    }

上面的帖子是关于 TaskEx.RunEx 的原因讨论,它与无法将更改放入 CTP 的核心 .NET 功能有关,但在最终发布时将被正确集成。 - Patrick McDonald
Task.RunEx 更改为 TaskEx.RunEx。Async CTP Task 类中既没有 Run() 也没有 RunEx()。它们都在 TaskEx 中。如果我错了,请纠正我。 - Gennady Vanin Геннадий Ванин
3个回答

13

当您想在线程池上下文中运行同步代码时,请使用TaskEx.Run

当您想在线程池上下文中运行异步代码时,请使用TaskEx.RunEx

Stephen Toub撰写了两篇博客文章,与行为差异有关:

这只是您创建任务的几个选项之一之一。如果您不必使用Run/RunEx,则不应该使用它们。使用简单的async方法,并仅在需要在后台运行某些内容时才使用Run/RunEx


事实上,那并不是真的... RunEx 与返回值 lambda 表达式有关... 而不是异步。Run 处理异步 lambda 很好。 - Firoso
2
在 .Net 4.5 DP 中,似乎只有 Task.Run(),没有 Ex - svick
6
TaskEx仅在CTP中使用。在.Net 4.5中,TaskEx的方法已经整合到Task中。 - Phil
1
@svick,@Phil:在.NET 4.5中,TaskEx.Run成为Task.Run的一个重载,而TaskEx.RunEx也成为Task.Run的一个重载。 - Stephen Cleary
1
@Firoso: RunEx执行内部任务的解包。如果您在VS2010中将异步lambda传递给Run,它会“正常工作”——如果您不关心何时完成。但是,如果您希望返回的Task在异步lambda完成时完成,则使用TaskEx。有关更多信息,请参见此博客文章此博客文章 - Stephen Cleary

1
你两个 DoWork() 方法的区别在于第一个方法(使用了 TaskEx.RunEx())根本不是异步的。它完全同步执行,启动另一个线程上的其他任务,并立即返回已完成的 Task。如果你 awaitWait() 这个任务,它 不会 等待内部任务完成。

0

在大多数情况下,Task.Run会生成一个新的线程。

需要注意的是,仅仅因为你将一个方法标记为async,并使用awaiters,这并不一定意味着会创建新的线程。很多情况下,完成操作会在调用它们的同一执行线程上进行调度。

这里的关键在于调度上下文(SchedulingContext)。如果它被设置为多线程单元(multithreaded apartment),那么完成操作将委托给线程池中的可行线程。如果你处于单线程单元(singlethreaded apartment)中,如WPF和WinForms UI代码所在的环境,那么它将返回到调用线程进行完成操作,允许直接在UI上进行工作,而不需要在代码中进行可见的线程调度。


Task.Run将任务排队到线程池中,通常不会启动新线程。await将其继续项调度到当前的SynchronizationContextTaskScheduler(而非线程)-请参见此文章末尾,该文章仍适用于CTP v3(和VS11 dev preview)。我还在我的异步介绍帖子中介绍了异步上下文。这个上下文与MTA或STA无关,但可能有一个支持STA或MTA的上下文。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接