我应该在同步和异步方法的C#循环中执行'Task.Wait()'

时间:2018-01-24 17:43:57

标签: c# asynchronous async-await

我想在循环中调用两种方法。 Step1()必须在调用Step2()之前完成。但是在循环中,Step1()可以在Step2()异步执行时启动。我是否应该等待Step2任务,然后才允许执行任何其他'Step2'任务,就像我在下面的代码中那样?

public MainViewModel()
{
    StartCommand = new RelayCommand(Start);
}

public ICommand StartCommand { get; set; }

private async void Start()
{
    await  Task.Factory.StartNew(() =>
    {
        Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Started processing.");
        for (int i = 0; i < 10; i++)
        {
            _counter++;
            string result = Step1(i);

            _step2Task?.Wait();     //Is this OK to do???

            Step2(result).ConfigureAwait(false);
        }

        _step2Task?.Wait();
        Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Finished processing.");
    });
}

private string Step1(int i)
{
    Thread.Sleep(5000);   //simulates time-consuming task
    Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Step 1 completed - Iteration {i}.");

    return $"Step1Result{i}";
}

private async Task Step2(string result)
{
    _step2Task = Task.Run(() =>
    {
        Thread.Sleep(4000);  //simulates time-consuming task
        Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Step 2 completed. - {result}");

    });
    await _step2Task;
}

1 个答案:

答案 0 :(得分:5)

不要做任何这些事情;你将有可能在整个地方遇到僵局。另外,不要将内容移动到线程上,除非它受CPU限制。

重新开始:

  • 查找每个长时间运行的同步方法(CPU密集型)并在其周围编写异步包装器。异步包装器应该获取工作线程,执行CPU密集型任务,并在执行完成时完成。 现在,您始终在任务方面拥有抽象,而不是线程

  • 将所有控制流逻辑移到UI线程上。

  • 在你所指的任何地方放置await“在等待任务完成之前必须执行的代码才会执行​​”。

如果我们这样做,您的代码会变得更简单:

// Return Task, not void
// Name async methods accordingly
private async Task StartAsync()
{
  Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Started processing.");
  Task task2 = null;
  for (int i = 0; i < 10; i++)
  {
     // We cannot do Step2Async until Step1Async's task 
     // completes, so await it.
     string result = await Step1Async(i);
     // We can't run a new Step2Async until the old one is done:
     if (task2 != null) {
       await task2;
       task2 = null;
     }
     // Now run a new Step2Async:
     task2 = Step2Async(result);
     // But *do not await it*.  We don't care if a new Step1Async
     // starts up before Step2Async is done.
  }
  // Finally, don't complete StartAsync until any pending Step2 is done.
  if (task2 != null) {
    await task2;
    task2 = null;
  }
  Console.WriteLine($"{DateTime.Now:hh:mm:ss.fff} - Finished processing.");
}

private string Step1(int i)
{
   // TODO: CPU intensive work here
}

private async Task<string> Step1Async(int i) {
  // TODO: Run CPU-intensive Step1(i) on a worker thread
  // return a Task<string> representing that work, that is
  // completed when the work is done.
}

private void Step2(string result)
{
  // TODO: CPU-intensive work here
}

private async Task Step2Async(string result) 
{
  // TODO: Again, make a worker thread that runs Step2
  // and signals the task when it is complete.
}

请记住, await是对工作流程的排序操作。这意味着在此任务完成之前不要继续此工作流程;去寻找其他工作流程

练习:如何编写代码来表示工作流程:

  • Step1必须在Step2
  • 之前完成
  • 任何数量的Step2可能同时运行
  • 所有Step2必须在开始完成
  • 之前完成