c# - 封装一个任务来记录异步函数的持续时间

标签 c# multithreading asynchronous task

如果我从一个完整的控制台应用开始:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApplication3
{
  internal class Program
  {
     private static void Main(string[] args)
     {
        var tas = new List<TaskAuditor<string>>();

        // I want to await these when they actually run so another thread can use it while
        // wait for (example) my EF/SQL to run Async
        tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 1)));
        tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 2)));

        var running = new Task<String>[2];

        foreach (var ta in tas)
        {
           running[0] = ta.Start();
        }

        var runningCount = tas.Count;

        while (runningCount > 0)
        {
           var idx = Task.WaitAny(running);
           runningCount--;

           var task = running[idx];

           var ta = tas.FirstOrDefault(t => t.Task == task);

           Console.WriteLine(ta.Duration.ToString());
        }

        foreach (var ta in tas)
        {
           Console.WriteLine(ta.Task.Result);
        }
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
     }

     public async Task<string> GetName(string constr, int id)
     {
        string result = id.ToString();

        // EF/SQL Async goes here
        if (string.IsNullOrWhiteSpace(constr))
        {
           await Task.Delay(1000 * id);
        }

        return result;
     }
  }

  public class TaskAuditor<T>
  {
     private Task<T> _task;
     private Stopwatch _sw = new Stopwatch();

     public TaskAuditor(Task<T> task)
     {
        _task = task;
     }

     public Task<T> Start()
     {
        _sw.Start();
        _task.Start();
        _sw.Stop();
        return _task;
     }

     public TimeSpan? Duration()
     {
        TimeSpan? result = null;
        if (!_sw.IsRunning)
        {
           result = _sw.Elapsed;
        }
        return result;
     }

     public Task<T> Task
     {
        get
        {
           return _task;
        }
     }
  }
}

DotNetFiddle Sample .

问题是我需要等待这个方法,它变成了一个我不太明白的异步噩梦。

最佳答案

首先,您的审核员需要做一些工作。正如我在我的博客上描述的那样,你 can't call Start on a Promise Task .如果要表示导致任务的操作,请使用 Func<Task<T>> .下一个问题是您在任务实际完成之前停止了秒表:

public class TaskAuditor<T>
{
  private Func<Task<T>> _func;
  private Stopwatch _sw = new Stopwatch();

  public TaskAuditor(Func<Task<T>> func)
  {
    _func = func;
  }

  public async Task<T> StartAsync()
  {
    _sw.Start();
    try
    {
      return await _func();
    }
    finally
    {
      _sw.Stop();
    }
  }

  public TimeSpan? Duration()
  {
    TimeSpan? result = null;
    if (!_sw.IsRunning)
    {
       result = _sw.Elapsed;
    }
    return result;
  }
}

接下来,您的调用方法应使用现代便利设施,例如 Task.WhenAll , 以及一个单独的方法来处理它们各自完成的结果,而不是试图将结果从列表中拉出来:

private static async Task<string> ProcessAsync(TaskAuditor<string> auditor)
{
  try
  {
    return await auditor.StartAsync();
  }
  finally
  {
    Console.WriteLine(auditor.Duration().ToString());
  }
}

private static async Task MainAsync()
{
  var tas = new List<TaskAuditor<string>>();
  tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 1)));
  tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 2)));

  var running = tas.Select(ta => ProcessAsync(ta));
  var results = await Task.WhenAll(running);
  foreach (var result in results)
  {
    Console.WriteLine(result);
  }
}

这也使代码保持异步直到它不能异步,即Main。 :

private static void Main()
{
  MainAsync().Wait();
  Console.WriteLine("Press any key to exit.");
  Console.ReadKey();
}

关于c# - 封装一个任务来记录异步函数的持续时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29306575/

相关文章:

c# - 异步读取图像文件

java - 在 Java 中运行时如何阻止主线程消耗完整的 CPU

c++ - 如何使用boost来运行线程另一个带有回调的对象函数

c# - SignalR 生成大量 403 错误

c# - 异步/等待 Lambda

c# - 如何在 FFMPEG 的特定位置设置水印?

c# - 如何从 C# csproj 文件引用解决方案的平台?

c# - 如何在 C# 中对模板使用 new 运算符

Java 线程 - 内存一致性错误

java - 线程状态 TimedWait。如何调试?