【问题标题】:Async/Await Usage in .NET.NET 中的异步/等待用法
【发布时间】:2021-08-17 04:29:05
【问题描述】:

我一直在做一个项目,看到了下面的代码。我是异步/等待世界的新手。据我所知,该方法中只有一个任务正在执行,那么为什么它用 async/await 装饰。使用 async/await 有什么好处,如果我删除 async/await 有什么缺点,即让它同步我有点困惑,所以任何帮助将不胜感激。

[Route("UpdatePersonalInformation")]
public async Task<DataTransferObject<bool>> UpdatePersonalInformation([FromBody] UserPersonalInformationRequestModel model)
{
    DataTransferObject<bool> transfer = new DataTransferObject<bool>();
    try
    {
        
        model.UserId = UserIdentity;

        transfer = await _userService.UpdateUserPersonalInformation(model);

    }
    catch (Exception ex)
    {
        transfer.TransactionStatusCode = 500;
        transfer.ErrorMessage = ex.Message;
    }
    return transfer;
}

服务代码

public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{

    DataTransferObject<bool> transfer = new DataTransferObject<bool>();
    await Task.Run(() =>
    {
        try
        {
            var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
            if (data != null)
            {
                var userProfile = mapper.Map<UserProfile>(model);
                userProfile.UpdatedBy = model.UserId;
                userProfile.UpdateOn = DateTime.UtcNow;
                userProfile.CreatedBy = data.CreatedBy;
                userProfile.CreatedOn = data.CreatedOn;
                userProfile.Id = data.Id;
                userProfile.TypeId = data.TypeId;
                userProfile.AspNetUserId = data.AspNetUserId;
                userProfile.ProfileStatus = data.ProfileStatus;
                userProfile.MemberSince = DateTime.UtcNow;
                if(userProfile.DOB==DateTime.MinValue)
                {
                    userProfile.DOB = null;
                }
                _userProfileRepository.Update(userProfile);

               

                transfer.Value = true;
            }
            else
            {
                transfer.Value = false;
                transfer.Message = "Invalid User";
            }

        }
        catch (Exception ex)
        {

            transfer.ErrorMessage = ex.Message;
        }

    });
    
    return transfer;
}

【问题讨论】:

标签: c# .net asynchronous asp.net-web-api async-await


【解决方案1】:

使用 async/await 有什么好处

通常,在 ASP.NET 上,async 的好处是您的服务器更具可扩展性 - 即可以处理比其他方式更多的请求。 "Synchronous vs. Asynchronous Request Handling" section of this article 更详细,但简短的解释是 async/await 释放了一个线程,以便它可以在异步工作完成时处理其他请求。

但是,在这种特定情况下,实际情况并非如此。在 ASP.NET 中使用 async/await 是好的和正确的,但是在 ASP.NET 中使用 Task.Run。因为Task.Run 发生的事情是另一个 线程用于在UpdateUserPersonalInformation 中运行委托。所以这不是异步的;它只是在后台线程上运行的同步代码。 UpdateUserPersonalInformation 将使用另一个线程池线程来运行其同步存储库调用,然后使用await 产生请求线程。所以它只是做一个线程切换,根本没有任何好处。

一个适当的实现将使存储库异步首先,然后UpdateUserPersonalInformation可以在没有Task.Run的情况下实现:

public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{
  DataTransferObject<bool> transfer = new DataTransferObject<bool>();
  try
  {
    var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
    if (data != null)
    {
      ...
      await _userProfileRepository.UpdateAsync(userProfile);
      transfer.Value = true;
    }
    else
    {
      transfer.Value = false;
      transfer.Message = "Invalid User";
    }
  }
  catch (Exception ex)
  {
    transfer.ErrorMessage = ex.Message;
  }

  return transfer;
}

【讨论】:

  • 这就是我想要的。谢谢!
【解决方案2】:

await 关键字仅表示当前函数的执行暂停 直到正在等待的任务完成。这意味着如果您删除async,该方法将继续执行并因此立即返回transfer 对象,即使UpdateUserPersonalInformation 任务未完成。

看看这个例子:

    private void showInfo()
    {
        Task.Delay(1000);
        MessageBox.Show("Info");
    }

    private async void showInfoAsync()
    {
        await Task.Delay(1000);
        MessageBox.Show("Info");
    }

在第一种方法中,MessageBox立即显示,因为新创建的任务(只等待指定的时间量)不等待。但是,第二种方法指定了await 关键字,因此MessageBox 仅在任务完成后显示(在示例中,经过 1000 毫秒后)。 但是,在两种情况中,延迟任务在后台异步运行,因此主线程(例如 UI)不会冻结。

【讨论】:

  • 如果我也删除任务会怎样,即使其同步。当它只执行一个任务时,它们之间有什么区别?
  • @SalmanAkbar 如果操作时间过长,同步函数将抛出 TimeOutException
【解决方案3】:

主要用到async-await机制的用法

  • 当您有一些需要一些时间的长计算过程并且您希望它在后台运行时
  • 在 UI 中,当您不想让主线程卡住时,这将反映在 UI 性能上。

您可以在此处阅读更多内容: https://docs.microsoft.com/en-us/dotnet/csharp/async

【讨论】:

    【解决方案4】:

    超时

    asyncawait 的主要用法是通过等待长操作完成来防止TimeOuts。然而,还有另一种鲜为人知但非常强大的。

    如果您不等待长时间操作,您将返回一个结果,例如 null,即使实际请求尚未完成。

    取消令牌

    异步请求有一个可以添加的默认参数:

    public async Task<DataTransferObject<bool>> UpdatePersonalInformation(
         [FromBody] UserPersonalInformationRequestModel model,
         CancellationToken cancellationToken){..}
    

    CancellationToken 允许在用户更改页面或中断连接时停止请求。一个很好的例子是用户有一个搜索框,每次输入一个字母时,你都会从你的 API 中过滤和搜索结果。现在想象一下用户输入了一个很长的字符串,比如 15 个字符。这意味着发送了 15 个请求,需要完成 15 个请求。即使前端没有等待前 14 个结果,API 仍在处理所有 15 个请求。

    取消令牌只是告诉 API 删除未使用的线程。

    【讨论】:

      【解决方案5】:

      我想加入这一点,因为大多数答案虽然很好,但没有指出何时使用和何时不使用的明确时间。 根据我的经验,如果您正在使用前端开发任何东西,请在期望其他线程的输出输入到您的 UI 时将 async/await 添加到您的方法中。这是处理多线程输出的最佳策略,应该赞扬微软在他们这样做时提出了这一点。如果没有 async/await,您将不得不添加更多代码来处理 UI 的线程输出(例如 Event、Event Handler、Delegate、Event Subscription、Marshaller)。 除非战略性地用于慢速外围设备,否则在其他任何地方都不需要它。

      【讨论】:

        猜你喜欢
        • 2013-11-13
        • 1970-01-01
        • 2014-02-15
        • 1970-01-01
        • 1970-01-01
        • 2013-04-15
        • 2014-12-09
        • 2014-05-13
        • 2016-06-18
        相关资源
        最近更新 更多