当你等待异步调用时,这真的是C#中的异步编程吗?

huangapple go评论70阅读模式
英文:

When you await on async call--is it really asynchronous programming in C#

问题

I have three tier .net web api application. each tier is implementing a async method. Each call is async and and we are using await at each step. My question is when you await at every step, doesn't it make the application synchronous & defeats the purpose ? What value async await really provides? Does it automatically uses multiple CPU cores?

Controller:

public async Task<IHttpActionResult> Get()
{
    return Ok(await _service.Create());
}

middle tier:

public async Task<MyObject> Create()
{
    return await _repository.CreateAsync(new MyDataObject());
}

DB:

public async Task<T> CreateAsync(T data)
{
    var o = _dbSet.Add(data);
    await _dbContext.SaveChangesAsync();
    return 100;
}
英文:

I have three tier .net web api application. each tier is implementing a async method. Each call is async and and we are using await at each step. My question is when you await at every step, doesn't it make the application synchronous & defeats the purpose ? What value async await really provides? Does it automatically uses multiple CPU cores?

Controller:

public async Task&lt;IHttpActionResult&gt; Get()
{
    return Ok(await _service.Create());
}

middle tier:

public async Task&lt;MyObject&gt; Create()
{
    return await _repository.CreateAsync(new MyDataObject());
}

DB

public async Task&lt;T&gt; CreateAsync(T data)
{
    var o = _dbSet.Add(data);
    await _dbContext.SaveChangesAsync();
    return 100;
}

答案1

得分: 5

在你的示例中,你确实在进行异步处理。你没有做的是并行处理。.NET文档并不总是很好地区分这两者。我只会在这里讨论异步。

async / await的主要思想(你的示例做得很好)是在有线程可以做有用的事情时,不要让线程闲置。

以你的CreateAsync示例为例:

public async Task<T> CreateAsync(T data)
{
    var o = _dbSet.Add(data);
    await _dbContext.SaveChangesAsync();
    return 100;
}

由于await,数据库的更改不会变得更快。那么好处在哪里呢?假设以下情况:

  • 你的应用程序有5个用户
  • 你的应用程序有2个可用线程(远远不够,但假设一下)
  • 每个数据库保存需要3秒

现在假设用户A和用户B同时保存记录。每个用户都有自己的线程(这是事实,不是假设)。如果你不使用await,那么用户A和B将持有你可用的两个线程,等待保存完成,需要3秒。没有更多的线程可用,所以用户C、D和E将在这段时间内被阻塞。

当你使用await时,线程会被释放,因为它们只是在等待。现在它们可以处理用户C、D和E。当保存完成时,线程将返回给用户A和B,并完成CreateAsync方法。

总之,当使用async / await正确时,n个线程可以处理比n个请求更多的任务。

这是一个简化的解释,但我真的希望很早以前我能得到一个简单的解释。希望这有所帮助。

英文:

In your example you're definitely doing asynchronous processing. What you're not doing is parallel processing. The .NET documentation doesn't always do a good job of differentiating between the two. I'll just cover asynchronous here.

The main idea behind async / await (which your examples do well) is to not have a thread sitting around doing nothing when it could be doing something useful.

Take your CreateAsync example:

public async Task&lt;T&gt; CreateAsync(T data)
{
    var o = _dbSet.Add(data);
    await _dbContext.SaveChangesAsync();
    return 100;
}

The database changes won't happen any faster because of the await. So what's the benefit? For the sake of argument assume the following:

  • Your app has 5 users
  • Your app has 2 threads available (way low, but play along)
  • Each database save takes 3 seconds

Now assume User A and User B save a record at the same time. Each has their own thread (that's a fact, not an assumption). If you aren't using await then users A and B will hold both of your available threads for 3 seconds just waiting for the save to complete. No more threads are available, so users C, D and E will be blocked for that period.

When you use await, the threads are released because they're just... waiting. Now they can take care of users C, D, and E. When the save completes the threads will come back for users A and B and finish the CreateAsync method.

Bottom line, when async / await is done properly n threads can fulfill way more than n requests.

This is a simplified explanation, but I really wish I'd gotten a simple explanation way back when. I hope this helps.

答案2

得分: 2

它允许您的 Web 服务器同时处理更多的请求;async/await 允许线程在代码等待数据库完成其工作(数据库或缓存、电子邮件服务、外部 API 调用等)时执行其他工作。

来自异步编程:ASP.NET 上 Async/Await 的简介

"(...) 假设系统中的请求依赖于某些外部资源,比如数据库或 Web API。当请求进来时,ASP.NET 会获取其中一个线程池线程并将其分配给该请求。因为它是同步编写的,请求处理程序将以同步方式调用该外部资源。这将阻塞请求线程,直到外部资源的调用返回。

...

异步请求处理程序的操作方式不同。当请求进来时,ASP.NET 会获取其中一个线程池线程并将其分配给该请求。这次,请求处理程序将以异步方式调用该外部资源。这会将请求线程返回到线程池,直到外部资源的调用返回。"

英文:

> What value async await really provides?

It allows your web server to process more requests at the same time; async/await allows the threads to do other work while the code waits for the db to do its work (db or cache, email service, external api call, etc.).

From Async Programming : Introduction to Async/Await on ASP.NET:

> (...) let’s say the requests in the system depend on some external resource, like a database or Web API. When a request comes in, ASP.NET takes one of its thread pool threads and assigns it to that request. Because it’s written synchronously, the request handler will call that external resource synchronously. This blocks the request thread until the call to the external resource returns.
>
> ...
>
> Asynchronous request handlers operate differently. When a request comes in, ASP.NET takes one of its thread pool threads and assigns it to that request. This time the request handler will call that external resource asynchronously. This returns the request thread to the thread pool until the call to the external resource returns.

答案3

得分: 1

当您在C#中等待异步方法时,并不一定意味着该方法会异步执行。它只是表示调用方法将被挂起,直到等待的任务完成。

C#中的async和await关键字用于简化编写异步代码的过程。它们允许您编写看起来像同步代码的代码,但在底层仍然是异步执行的。

要理解async和await的工作原理,重要的是要知道异步方法实际上返回一个表示异步操作的Task或Task<T>对象。await操作符用于挂起调用方法的执行,直到等待的Task完成。

然而,仅仅因为一个方法被标记为async,并不一定意味着它将以异步方式执行。方法的实际执行取决于Task的实现方式以及它是否在线程池线程上运行。

例如,如果Task在线程池线程上运行,它将以异步方式执行。另一方面,如果使用Task.Run方法实现Task,它将在单独的线程池线程上同步执行。

简而言之,C#中的async和await关键字允许您编写更容易阅读和维护的异步代码,但并不保证代码将以异步方式执行。

英文:

When you await an async method in C#, it does not necessarily mean that the method is executed asynchronously. It simply means that the calling method will be suspended until the awaited task completes.

The async and await keywords in C# are used to simplify the process of writing asynchronous code. They allow you to write code that looks and reads like synchronous code, while still being asynchronous under the hood.

To understand how async and await work, it is important to know that an async method actually returns a Task or Task<T> object, which represents the asynchronous operation. The await operator is used to suspend the execution of the calling method until the awaited Task completes.

However, just because a method is marked async does not necessarily mean that it will be executed asynchronously. The actual execution of the method depends on how the Task is implemented and whether it is running on a thread pool thread or not.

For example, if the Task is running on a ThreadPool thread, it will be executed asynchronously. On the other hand, if the Task is implemented using the Task.Run method, it will be executed synchronously on a separate ThreadPool thread.

In short, the async and await keywords in C# allow you to write asynchronous code that is easier to read and maintain, but they do not guarantee that the code will be executed asynchronously.

答案4

得分: 1

每次调用都是async,并且在每个步骤中我们都使用await我的问题是,当你在每个步骤都使用await时,它不会使应用程序变成同步,并且失去了目的吗?

首先,同步和异步是方法或执行流的属性,而不是整个应用程序的属性。在你的情况下,你在每个级别和每个步骤上都使用await来等待你启动的所有异步操作(Task),因此每个Web请求的执行流都是异步的。然而,它仍然是单个执行流。代码以顺序/序列化方式执行。在单个Web请求的上下文中,不会发生并发。

与经典且熟悉的同步执行流的区别在于,执行流不会被强制在同一个线程上从头到尾运行。不同的线程可能参与执行,但不会同时有多个线程。在每个await点可能会发生线程切换,它是否会发生取决于可能不在您直接控制之下的各种因素。异步流最有趣的方面在于,时间上,执行的大部分部分通常不在任何线程上运行。例如,当_dbContext.SaveChangesAsync()正在执行其工作时,你的进程中没有一个线程被阻塞等待工作完成。这意味着你的Web应用程序可以具有大量并发执行流(Web请求),只使用少量线程。最终,这将增加应用程序的可伸缩性:你可以使用更少的硬件为更多用户提供服务。

英文:

> Each call is async and and we are using await at each step. My question is when you await at every step, doesn't it make the application synchronous & defeats the purpose?

First of all, synchronous and asynchronous is a property of a method or an execution flow, not a property of the application as a whole. In your case you await on every level and every step all asynchronous operations (Tasks) that you start, so the execution flow of each web request is asynchronous. It's still a single execution flow though. The code is executed in a sequential/serialized fashion. In the context of a single web request, no concurrency is happening.

The difference from a classic and familiar synchronous execution flow is that the flow is not forced to run on the same thread from start to finish. Different threads might be involved in the execution, but not more than one thread at the same time. A thread-switch might occur at each await point, and whether it will happen depends on various factors that might not be under your direct control. The most interesting aspect of an asynchronous flow is that, time-wise, large parts of the execution are usually not running on any thread. For example while the _dbContext.SaveChangesAsync() is doing its job, there is no thread in your process that is blocked waiting for this work to be done. This means that your web application can have a large number of concurrent executions flows (web requests), using only a small number of threads. Which eventually results in increased scalability for your application: you can serve more users with less hardware.

答案5

得分: 1

相对较新的开发者,直接使用 async/await 进行异步编程可能会有这种困惑。要理解和欣赏 async/await 带来的价值,一个人需要稍微回顾一下以前异步代码是如何编写的。

在引入这些关键字之前,在 C# 中仍然可以进行异步编程。传统上(.NET Framework 2.0 以后),存在两种模式:

事件驱动的异步模式(EAP),您可以订阅事件:

o.ReadCompleted += (o, e) => { // 异步调用在 ReadAsync 完成后执行 }; 
o.ReadAsync(buffer, count); // 开始异步过程

任务驱动的异步模式(TAP),这是一种类似 Promise 的接口,您可以使用 thencatch 设置完成操作。

o.ReadAsync(buffer, count)
  .then((result) => { // 异步调用在 ReadAsync 完成后执行 });

因此,服务器上进行异步编程的优势在于调用线程不会在 Read 上被阻塞,而现在 Read 是一个异步调用,而不是同步调用。线程可以执行其他任务或处理其他请求。

当 I/O 绑定的 Read 最终完成时,将调用完成委托,可能在不同的线程上。这样就有了更多的线程可用,使服务更具可伸缩性。这是异步编程的优势。

然而,上面的两种模式对于开发者有一个不利之处,那就是代码变得难以阅读和"里外颠倒",因为它不是按顺序编写的。

async/await 关键字只是让我们保持异步编程的相同优点,同时使代码更易阅读。

现在我们只需编写:

var result = await o.ReadAsync(buffer, count);
// 这里是以前必须在完成委托内部编写的代码
// 但现在更易阅读,处理异常也更自然

因此,尽管对开发者来说看起来是顺序的,但仍然是异步的。编译器将 async 方法转换为一个 状态机,其行为类似于以前编写的代码,但将所有复杂的细节隐藏在开发者之外。与完成委托的行为类似,await 后面的代码也可以在不同的线程上恢复执行。

英文:

Relatively new developers who come to asynchronous programming directly using async/await may have this confusion. To understand and appreciate the value of what async/await brings to table, one has to go slightly back in time and review how asynchronous code was written then.

Before the keywords were introduced in C#, asynchronous programming was still possible. Traditionally (.NET Framework 2.0 later), there were two patterns:

Event-based Asynchronous Pattern (EAP), where you subscribed to an event:

o.ReadCompleted += (o, e) =&gt; { // asynchronously called after ReadAsync completion }; 
o.ReadAsync(buffer, count); // begin the async process

Task-based Asynchronous Pattern (TAP), which is Promise like interface where you can setup completions with then and catch.

o.ReadAsync(buffer, count)
  .then( (result) =&gt; { // asynchronously called after ReadAsync completion } );

So, advantage of asynchronous programming on the server is that the calling thread is not blocked on the Read, which is now an asynchronous call as against a synchronous call. The thread can do some other tasks / or serve other requests.

When I/O bound Read eventually completes, the completion delegate will be called, possibly over a different thread. This way there are more threads available which makes the service more scalable. This is advantage of asynchronous programming.

However, the two patterns above have a disadvantage for the developer that the code becomes unreadable and inside-out because it is not written sequentially.

The async/await keywords just let us keep the same advantages of asynchronous programming, while making the code readable.

Now we just write:

var result = await o.ReadAsync(buffer, count);
// here comes code that we had to previously write inside the completion delegate
// but now its more readable, and much natural to handle exceptions

So although it looks sequential to developer it is still asynchronous. The compiler converts the async method into a state machine which behaves similar to code previously written, but hides away all the complex details from the developer. Similar to the completion delegate behavior, the code written after await could also be resumed on a different thread.

huangapple
  • 本文由 发表于 2023年1月6日 10:52:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75026489.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定