为什么任务在执行下一个代码之前等待Task.Run而不是Task.Factory.StartNew?

Sto*_*tip 2 c# wpf task-parallel-library async-await

至少当我在我的代码中实现它时,我不得不修改StartNew Task以获得相同的行为.在我的视图中有一个开始按钮.它的IsEnabled属性绑定到View Model中的Boolean.在不添加await task.ContinueWith(_ => true);return true;移出try块的情况下,PopulateListStartNew任务不会等待,因此按钮保持启用状态.我更喜欢使用,Task.Factory.StartNew因为传递TaskScheduler会产生更易读的代码(没有Dispatcher混乱).记录是一个ObservableCollection.

我认为Task.Run基本上是一个快捷方式(每个Task.Run vs Task.Factory.StartNew.无论如何,我想更好地理解行为上的差异,并且肯定会感谢任何有关使我的示例代码更好的建议.

public async Task<bool> PopulateListTaskRun(CancellationToken cancellationToken)
{
    try
    {
        await Task.Run(async () =>
            {
                // Clear the records out first, if any
                Application.Current.Dispatcher.InvokeAsync(() => Records.Clear());


                for (var i = 0; i < 10; i++)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    // Resharper says do this to avoid "Access to modified closure"
                    var i1 = i;

                    Application.Current.Dispatcher.InvokeAsync(() =>
                        {
                            Records.Add(new Model
                                {
                                    Name = NamesList[i1],
                                    Number = i1
                                });

                            Status = "cur: " +
                                        i1.ToString(
                                            CultureInfo.InvariantCulture);
                        });

                    // Artificial delay so we can see what's going on
                    await Task.Delay(200);
                }

                Records[0].Name = "Yes!";
            }, cancellationToken);

        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

public async Task<bool> PopulateListStartNew(CancellationToken cancellationToken, TaskScheduler taskScheduler)
{
    try
    {
        var task = await Task.Factory.StartNew(async () =>
            {
                // Clear the records out first, if any
                Records.Clear();


                for (var i = 0; i < 10; i++)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    Records.Add(new Model
                        {
                            Name = NamesList[i],
                            Number = i
                        });

                    Status = "cur: " +
                                i.ToString(
                                    CultureInfo.InvariantCulture);


                    // Artificial delay so we can see what's going on
                    await Task.Delay(200);
                }

                Records[0].Name = "Yes!";
            }, cancellationToken, TaskCreationOptions.None, taskScheduler);

        // Had to add this
        await task.ContinueWith(_ => true);
    }
    catch (Exception)
    {
        return false;
    }

    // Had to move this out of try block
    return true;
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 5

您在问题中发布的链接有答案:Task.Run理解和解包async Task代表,而StartNew返回Task<Task>代替,您必须通过调用Unwrap或执行双重解包来解开自己await.

但是,我建议您完全重写代码如下.笔记:

  • 不要用Dispatcher.使用正确编写的async代码不应该需要它.
  • 将所有后台工作方法和异步操作视为UI线程的"服务".因此,您的方法将根据需要定期返回UI上下文.

像这样:

public async Task<bool> PopulateListTaskRunAsync(CancellationToken cancellationToken)
{
  try
  {
    // Clear the records out first, if any
    Records.Clear();

    for (var i = 0; i < 10; i++)
    {
      cancellationToken.ThrowIfCancellationRequested();

      Records.Add(new Model
      {
        Name = NamesList[i],
        Number = i
      });

      Status = "cur: " + i.ToString(CultureInfo.InvariantCulture);

      // Artificial delay so we can see what's going on
      await Task.Delay(200);
    }

    Records[0].Name = "Yes!";
    return true;
  }
  catch (Exception)
  {
    return false;
  }
}
Run Code Online (Sandbox Code Playgroud)