简体   繁体   English

.NET 中的异步/等待使用

[英]Async/Await Usage in .NET

I've been working on a project and saw the below code.我一直在做一个项目,看到了下面的代码。 I am new to the async/await world.我是异步/等待世界的新手。 As far as I know, only a single task is performing in the method then why it is decorated with async/await.据我所知,该方法中只有一个任务正在执行,那么为什么它用 async/await 装饰。 What benefits I am getting by using async/await and what is the drawback if I remove async/await ie make it synchronous I am a little bit confused so any help will be appreciated.使用 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;
}

Service code服务代码

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;
}

What benefits I am getting by using async/await使用 async/await 有什么好处

Normally, on ASP.NET, the benefit of async is that your server is more scalable - ie, can handle more requests than it otherwise could.通常,在 ASP.NET 上, async的好处是您的服务器更具可扩展性 - 即,可以处理比其他方式更多的请求。 The "Synchronous vs. Asynchronous Request Handling" section of this article goes into more detail, but the short explanation is that async / await frees up a thread so that it can handle other requests while the asynchronous work is being done. 本文的“同步与异步请求处理”部分更详细,但简短的解释是async / await释放了一个线程,以便它可以在异步工作完成时处理其他请求。

However, in this specific case, that's not actually what's going on.但是,在这种特定情况下,实际情况并非如此。 Using async / await in ASP.NET is good and proper, but using Task.Run on ASP.NET is not .在 ASP.NET 中使用async / await是好的和正确的,但是在 ASP.NET 上使用Task.Run不是 Because what happens with Task.Run is that another thread is used to run the delegate within UpdateUserPersonalInformation .因为Task.Run发生的事情是另一个线程用于在UpdateUserPersonalInformation中运行委托。 So this isn't asynchronous;所以这不是异步的; it's just synchronous code running on a background thread.它只是在后台线程上运行的同步代码。 UpdateUserPersonalInformation will take another thread pool thread to run its synchronous repository call and then yield the request thread by using await . UpdateUserPersonalInformation将采用另一个线程池线程来运行其同步存储库调用,然后使用await产生请求线程。 So it's just doing a thread switch for no benefit at all.所以它只是做一个线程切换,根本没有任何好处。

A proper implementation would make the repository asynchronous first , and then UpdateUserPersonalInformation can be implemented without Task.Run at all:一个正确的实现会首先使存储库异步,然后可以在没有Task.Run的情况下实现UpdateUserPersonalInformation

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;
}

The await keyword only indicates that the execution of the current function is halted until the Task which is being awaited is completed . await关键字仅表示当前 function 的执行被暂停,直到正在等待的任务完成 This means if you remove the async , the method will continue execution and therefore immediately return the transfer object, even if the UpdateUserPersonalInformation Task is not finished.这意味着如果您删除async ,该方法将继续执行并因此立即返回transfer object,即使UpdateUserPersonalInformation任务未完成。

Take a look at this example:看看这个例子:

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

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

In the first method, the MessageBox is immediately displayed, since the newly created Task (which only waits a specified amount of time) is not awaited .在第一种方法中, MessageBox立即显示,因为新创建的 Task(它只等待指定的时间)不是 awaited However, the second method specifies the await keyword, therefore the MessageBox is displayed only after the Task is finished (in the example, after 1000ms elapsed).但是,第二种方法指定了await关键字,因此MessageBox仅在 Task 完成后显示(在示例中,经过 1000 毫秒后)。 But, in both cases the delay Task is ran asynchronously in the background, so the main thread (for example the UI) will not freeze.但是,在这两种情况下,延迟任务都是在后台异步运行的,因此主线程(例如 UI)不会冻结。

The usage of async-await mechanism mainly used async-await机制的使用主要用到

  • when you have some long calculation process which takes some time and you want it to be on the background当您有一些较长的计算过程需要一些时间并且您希望它在后台运行时
  • in UI when you don't want to make the main thread stuck which will be reflected on UI performance.在 UI 中当您不想让主线程卡住时,这将反映在 UI 性能上。

you can read more here: https://docs.microsoft.com/en-us/dotnet/csharp/async你可以在这里阅读更多: https://docs.microsoft.com/en-us/dotnet/csharp/async

Time Outs超时

The main usages of async and await operates preventing TimeOuts by waiting for long operations to complete. asyncawait的主要用途是通过等待长操作完成来防止TimeOuts However, there is another less known, but very powerful one.然而,还有另一种鲜为人知但非常强大的。

If you don't await long operation, you will get a result back, such as a null, even though the actual request as not completed yet.如果您不等待长时间操作,即使实际请求尚未完成,您也会得到一个结果,例如 null。

Cancellation Tokens取消令牌

Async requests have a default parameter you can add:异步请求有一个可以添加的默认参数:

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

A CancellationToken allows the request to stop when the user changes pages or interrupts the connection. CancellationToken允许在用户更改页面或中断连接时停止请求。 A good example of this is a user has a search box, and every time a letter is typed you filter and search results from your API.一个很好的例子是用户有一个搜索框,每次输入一个字母时,您都会从 API 中过滤和搜索结果。 Now imagine the user types a very long string with say 15 characters.现在想象一下用户输入了一个很长的字符串,比如 15 个字符。 That means that 15 requests are sent and 15 requests need to be completed.这意味着发送了 15 个请求,需要完成 15 个请求。 Even if the front end is not awaiting the first 14 results, the API is still doing all the 15 requests.即使前端没有等待前 14 个结果,API 仍然在执行所有 15 个请求。

A cancellation token simply tells the API to drop the unused threads.取消令牌只是告诉 API 丢弃未使用的线程。

I would like to chime in on this because most answers although good, do not point to a definite time when to use and when not.我想加入这一点,因为大多数答案虽然很好,但并没有指出何时使用和何时不使用的明确时间。 From my experience, if you are developing anything with a front-end, add async/await to your methods when expecting output from other threads to be input to your UI.根据我的经验,如果您正在使用前端开发任何东西,请在期望来自其他线程的 output 输入到您的 UI 时,将 async/await 添加到您的方法中。 This is the best strategy for handling multithread output and Microsoft should be commended to come out with this when they did.这是处理多线程 output 的最佳策略,当他们这样做时,应该赞扬微软提出这一点。 Without async/await you would have to add more code to handle thread output to UI (eg Event, Event Handler, Delegate, Event Subscription, Marshaller).如果没有 async/await,您将不得不添加更多代码来处理线程 output 到 UI(例如 Event、Event Handler、Delegate、Event Subscription、Marshaller)。 Don't need it anywhere else except if using strategically for slow peripherals.除非战略性地用于慢速外围设备,否则在其他任何地方都不需要它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM