【问题标题】:Make methods run asynchronously使方法异步运行
【发布时间】:2021-04-15 01:15:33
【问题描述】:

有人可以看看这段代码并告诉我我做错了什么。在我看来,这 3 种方法应该以相同的方式运行,但它们一个接一个地运行。请看写在控制台上的时间。在我看来,所有 Console.WriteLine 都应该显示 ~60ms。 下面的代码示例:

private async void GetOneCombination(string firstMarket, string secondMarket, string thirdMarket, decimal amountOfFirstCurrency)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();

        Task<GetMarketResponse> result = _accessMethods.GetOrderbook(firstMarket);
        Console.WriteLine(sw.ElapsedMilliseconds); // ~60ms
        Task<GetMarketResponse> result1 = _accessMethods.GetOrderbook(secondMarket);
        Console.WriteLine(sw.ElapsedMilliseconds); // ~130 ms
        Task<GetMarketResponse> result2 = _accessMethods.GetOrderbook(thirdMarket);
        Console.WriteLine(sw.ElapsedMilliseconds); // ~200 ms
        var getMarketResponses = await Task.WhenAll(result, result1, result2);

    }

编辑: 老实说,我认为这个方法里面是什么并不重要,我认为无论里面做什么,它都会同时完成3次

  public async Task<GetMarketResponse> GetOrderbook(string market = "USD")
    {
        var address = AddressBook._orderbook + market;
        var response = MethodExecutionTimeMeasurer.Invoke(() => 
            _client.ExecuteGetAsyncPublic<GetMarketResponse>(address), out timespan);
        _logger.LogInformation(string.Format("OrderBook requested for [{0}], response message: {1}. Time[ms]:{2}", 
            address, 
            response.Status,
            timespan));
        return response; 
    }

和 ExecuteGetAsyncPublic:

   public async Task<T> ExecuteGetAsyncPublic<T>(string method)
        where T : IBasicResponse
    {
        var response = await _httpClient.GetAsync(method).ConfigureAwait(false);
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        var responseData = JsonConvert.DeserializeObject<T>(json);
        return responseData;
    }

方法执行时间测量器

public static class MethodExecutionTimeMeasurer
{
    public static T Invoke<T>(Func<Task<T>> action, out TimeSpan timeSpan)
    {
        var timer = Stopwatch.StartNew();
        var res = action.Invoke();
        res.Wait();
        timer.Stop();
        timeSpan = timer.Elapsed;
        return res.Result;
    }

    public static void Invoke(Action action, out TimeSpan timeSpan)
    {
        var timer = Stopwatch.StartNew();
        action.Invoke();
        timer.Stop();
        timeSpan = timer.Elapsed;
    }
}

【问题讨论】:

  • 如果你var resultX = await _accessMethods.GetOrderbook() 3 次,它会显示什么?也显示GetOrderbook的代码
  • 作为旁注,avoid async void。请改用async Task。关于您的主要问题,您能否对其进行编辑并包含GetOrderbook 方法?
  • 除了@Theodor Zoulias 的注释(这里最好返回一个 Task 而不是 void ),我同意,在这种情况下,我猜方法的实现是问题所在(但如果没有看到就很难看到实施)。你可以很容易地弄清楚这一点,将每个呼叫包装在 Task.Run(() =&gt; _access.methods.Getorderbook(...)); 中(仅用于测试)
  • 感谢回复,我编辑了帖子。
  • 是的,我在你写这篇文章的时候添加了它。我有这个 res.Stop() 似乎是问题所在。但是你能解释一下为什么吗?我是异步方法的新手。

标签: c# .net multithreading asynchronous task


【解决方案1】:

这里有两个问题:

  1. GetOrderbook 方法具有异步签名,但其实现是同步的。您可能会收到关于缺少 await 运算符的 async 方法的警告。

  2. MethodExecutionTimeMeasurer.Invoke 有一个参数Func&lt;Task&lt;T&gt;&gt; action(异步委托),但创建的TaskWait 方法同步等待。所以在任务执行过程中,当前线程被阻塞了。

三个_accessMethods.GetOrderbook 调用中的每一个都返回一个已完成的任务,然后组合任务Task.WhenAll(result, result1, result2) 在创建时也已完成,简而言之,从当前线程的角度来看,没有任何东西在同步运行a。这个案例和昨天问的一个问题很相似,check it out

【讨论】:

    【解决方案2】:

    调用异步任务方法不会立即在新线程上启动。它将在被调用的线程上运行,直到遇到等待。

    so for example
    var task = DoSomething();
    
    public async Task DoSomething()
    {
        // MAIN THREAD
    
        await Task.Delay(1);
    
        // WORKER THREAD
    
    }
    
    

    如果你这样做,它可能会起作用

     public async Task<GetMarketResponse> GetOrderbook(string market = "USD")
        {
            await Task.Delay(1);
            var address = AddressBook._orderbook + market;
            var response = MethodExecutionTimeMeasurer.Invoke(() => 
                _client.ExecuteGetAsyncPublic<GetMarketResponse>(address), out timespan);
            _logger.LogInformation(string.Format("OrderBook requested for [{0}], response message: {1}. Time[ms]:{2}", 
                address, 
                response.Status,
                timespan));
            return response; 
        }
    
    

    您可以采取的另一种方法是

    Parallel.Invoke(
    () => _accessMethods.GetOrderbook(firstMarket).Wait(),
    () => _accessMethods.GetOrderbook(secondMarket).Wait(),
    () => _accessMethods.GetOrderbook(thirdMarket).Wait(),
    );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-02
      • 1970-01-01
      • 1970-01-01
      • 2020-05-13
      • 1970-01-01
      相关资源
      最近更新 更多