c# webapi: Await Task.Run vs more granualar await

2019-06-14 03:44发布

I'm using async/await in WebApi Controllers according to this article: https://msdn.microsoft.com/en-us/magazine/dn802603.aspx

Hava look at this simplified code in my controller:

DataBaseData = await Task.Run( () => GetDataFunction()  );

GetDataFunction is a function that will open a database connection, open a reader and read the data from the database.

In many examples I see it handled differently. The GetDataFunction itself is awaited. And within the function every single step is awaited. For example:

  • connection.OpenAsync
  • reader.ReadAsycnc
  • reader.IsDBNullAsync

Why is this good practice? Why not just start a thread for the whole database access (with Task.Run)?

Update: Thanks for the help. Now I got it. I did not get, that the asynchronous Api's do not start threads themselves. This really helped: blog.stephencleary.com/2013/11/there-is-no-thread.html

3条回答
Rolldiameter
2楼-- · 2019-06-14 04:22

It's all about resource management and sharing.

When using connection.Open() method the thread that calls it has to wait for the connection to actually open. While waiting it does nothing but consume the CPU slice allocated to it by the operating system. However, when you await connection.OpenAsync() the thread frees up its resources and the OS can redistribute them to other threads.

Now, there's a catch: to actually gain from asynchronous calls they should take longer than the time it takes for the OS to switch contexts otherwise it will incur a drop in application performance.

The practice of awaiting on connection.OpenAsync(), reader.ReadAsync() etc. is due to the fact that these are potentially long running operations. In a system where de database resides on another machine and it is under a heavy load of requests, connecting to the database and getting the results of a query will take some time. Instead of blocking the CPU while waiting on the results to arrive why not allocate that time slice to another worker thread that waits in the scheduler queue to render the response for another client?

Now, about starting another thread for data access: don't do that!. Each new thread gets allocated about 1MB of memory space so beside wasting CPU time while waiting for database operations to finish you'll be wasting memory also. Furthermore, having such a heavy memory footprint would require a lot more runs for Garbage Collector which will freeze all of your threads when running.

Using Task.Run() will schedule the data access operations on a thread from the ThreadPool but you will lose the benefit of sharing the CPU time with another thread while waiting for database server to respond.

查看更多
男人必须洒脱
3楼-- · 2019-06-14 04:30

The article you linked to states:

You can kick off some background work by awaiting Task.Run, but there’s no point in doing so. In fact, that will actually hurt your scalability by interfering with the ASP.NET thread pool heuristics... As a general rule, don’t queue work to the thread pool on ASP.NET.

In other words, avoid Task.Run on ASP.NET.

Why is this good practice? Why not just start a thread for the whole database access (with Task.Run)?

It's because the asynchronous APIs do not use other threads. The entire point of async/await is to free up the current thread; not use another thread. I have a blog post describing how async works without needing threads.

So, the Task.Run (or custom thread) approach will use up (block) a thread while getting data from the database. Proper asynchronous methods (e.g., EF6 or the ADO.NET asynchronous APIs) do not; they allow the request thread to be used for other requests while that request is waiting for the database response.

查看更多
ゆ 、 Hurt°
4楼-- · 2019-06-14 04:35

I assume you are wondering why using

await Task.Run(() => GetDataFunction());

instead of

await GetDataFunction();

As you can see in Task.Run(Func) under the section Remarks there is written:

The Run(Func) method is used by language compilers to support the async and await keywords. It is not intended to be called directly from user code.

That means, you should use await GetDataFunction.

查看更多
登录 后发表回答