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

标签 c# wpf task-parallel-library async-await

至少当我在代码中实现它时,我必须修改 StartNew Task 才能获得相同的行为。在我的 View 中有一个开始按钮。它的 IsEnabled 属性绑定(bind)到 View 模型中的 bool 值。如果不添加 await task.ContinueWith(_ => true); 并将 return true; 移出 try block ,PopulateListStartNew Task 不会等待,因此按钮保持启用状态。我更喜欢使用 Task.Factory.StartNew 因为传递 TaskScheduler 可以使代码更具可读性(没有 Dispatcher 困惑)。 Records 是一个 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;
}

最佳答案

您在问题中发布的链接有答案: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;
  }
}

关于c# - 为什么任务在执行下一个代码之前等待Task.Run而不是Task.Factory.StartNew?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17202314/

相关文章:

c# - 加入,然后使用 linq 查询将列表选择到列表中

java - C# 数组索引

c# - 从 native 调用 C# 来检索字符串

javascript - WebView.InvokeScriptAsync 不适用于通用应用程序

wpf - DataContext 和 Caliburn

任何数据(绑定(bind))更改时的 WPF DataTrigger

wpf - 是否可以让 CommandManager 仅重新查询特定 WPF 命令而不是全部命令?

c# - TPL .ContinueWith 在执行大量任务时优先

c# - 异步等待不起作用。为什么?

c# - .NET 编译器——是否内置了嵌套循环优化?