Architecture for async / wait

If you use async / await at a lower level in your architecture, whether it is necessary to “pop up” async / await calls completely, this is inefficient, since you basically create a new thread for each level (asynchronously invoking an asynchronous function for each level, or it doesn’t is of particular importance and depends only on your preferences?

I am using EF 6.0-alpha3 so that I have async methods in EF.

My repository is this:

public class EntityRepository<E> : IRepository<E> where E : class { public async virtual Task Save() { await context.SaveChangesAsync(); } } 

Now my business layer is this:

 public abstract class ApplicationBCBase<E> : IEntityBC<E> { public async virtual Task Save() { await repository.Save(); } } 

And then, of course, my method in my interface will have the same pattern when called.

It:

  • is necessary
  • negative result
  • just a matter of preference

Even if this is not used in separate layers / projects, then the same questions apply if I call nested methods in the same class:

  private async Task<string> Dosomething1() { //other stuff ... return await Dosomething2(); } private async Task<string> Dosomething2() { //other stuff ... return await Dosomething3(); } private async Task<string> Dosomething3() { //other stuff ... return await Task.Run(() => ""); } 
+56
c # asynchronous async-await
Mar 19 '13 at 15:33
source share
2 answers

If you use async / await at a lower level in your architecture, is it necessary to “pop up” async / await calls completely, inefficiently, since you basically create a new thread for each level (asynchronously calling an asynchronous function for each level, or it doesn’t have special significance and depends only on your preferences?

This question offers a couple of areas of misunderstanding.

Firstly, you do not create a new thread every time you call an asynchronous function.

Secondly, you do not need to declare an async method, just because you are calling an asynchronous function. If you are happy with a job that is already returning, just return this from a method that does not have an async modifier:

 public class EntityRepository<E> : IRepository<E> where E : class { public virtual Task Save() { return context.SaveChangesAsync(); } } public abstract class ApplicationBCBase<E> : IEntityBC<E> { public virtual Task Save() { return repository.Save(); } } 

It will be a little more efficient, since it is not connected with the creation of a state machine for a very small reason - but more importantly, it is simpler.

Any async method in which you have one await expression waiting for Task or Task<T> , right at the end of the method without further processing, would be better written without using async / await. So:

 public async Task<string> Foo() { var bar = new Bar(); bar.Baz(); return await bar.Quux(); } 

better to write like:

 public Task<string> Foo() { var bar = new Bar(); bar.Baz(); return bar.Quux(); } 

(Theoretically, there is a very slight difference in the tasks being created and, therefore, that callers can add continuations, but in the vast majority of cases you will not notice any difference.)

+55
Mar 19 '13 at 15:37
source share

is this inefficient as you basically create a new thread for each level (asynchronously invoking an asynchronous function for each level or does it not really matter and depends only on your preferences?)

No. Asynchronous methods do not necessarily use new threads. In this case, since the main asynchronous method call is the IO binding method, there really should not be any new threads.

It:

 1. necessary 

For asynchronous calls, you need to bubble calls if you want the operation to be asynchronous. However, this is really preferable because it allows you to fully use asynchronous methods, including compiling them together throughout the stack.

 2. negative on performance 

No. As I said, this does not create new threads. There is some overhead, but much of this can be minimized (see below).

 3. just a matter of preference 

Not if you want to keep this asynchronous. You must do this to keep asynchronous things on the stack.

Now, you can do to improve perf. Here. If you just wrap the asynchronous method, you do not need to use language functions - just return Task :

 public virtual Task Save() { return repository.Save(); } 

The repository.Save() method already returns Task - you do not need to wait for it just to return it to Task . This will support a more efficient method.

You can also use "low level" asynchronous methods using ConfigureAwait to prevent the need to use a call synchronization context:

 private async Task<string> Dosomething2() { //other stuff ... return await Dosomething3().ConfigureAwait(false); } 

This greatly reduces the overhead associated with each await , unless you need to worry about the calling context. This is usually the best option when working on "library" code, since the "external" await will capture the user interface context. The "internal" work of the library usually does not concern the synchronization context, therefore it is better not to write this.

Finally, I would caution you against one of your examples:

 private async Task<string> Dosomething3() { //other stuff ... // Potentially a bad idea! return await Task.Run(() => ""); } 

If you create an async method that internally uses Task.Run to “create asynchrony” around something that is not asynchronous, you effectively complete the synchronous code in the async method. This will use the ThreadPool thread, but it can “hide” the fact that it does this, which makes the API incorrect. It is often better to leave the Task.Run call to the highest level calls and let the main methods remain synchronous if they really cannot use the asynchronous IO or some unloading tools other than Task.Run . (This is not always the case, but the code is “asynchronous”, transcoded through synchronous code through Task.Run , then returned via async / await, is often a sign of an erroneous design.)

+27
Mar 19 '13 at 15:36
source share



All Articles