Contract asynchronous and synchronization code

There are many questions asking if mixing asynchronous and synchronizing code should be done.

Most answers say that it is a bad idea to expose sync wrappers for asynchronous methods and to expose asynchronous wrappers for synchronization methods.

However, none of the answers relate to a specific scenario in which you should mix asynchronous and synchronizing code, and how to avoid common errors that arise because of this.

See the following example:

class Program { static void Main(string[] args) { IContract signatory = new SyncSignatory(); signatory.FullfillContractAsync().Wait(); signatory = new AsyncSignatory(); signatory.FullfillContractAsync().Wait(); } } 



 using System.Threading.Tasks; interface IContract { Task FullfillContractAsync(); } 



 using System.Threading.Tasks; class AsyncSignatory : IContract { public async Task FullfillContractAsync() { await Task.Delay(5000); } } 



 using System.Threading; using System.Threading.Tasks; class SyncSignatory : IContract { public Task FullfillContractAsync() { Thread.Sleep(5000); return Task.FromResult<object>(null); } } 

Or:

 using System.Threading; using System.Threading.Tasks; class SyncSignatory : IContract { public Task FullfillContractAsync() { return Task.Run(() => Thread.Sleep(5000)); } } 

In this example, SyncSignatory and AsyncSignatory are two interchangeable classes because they perform similar functions, but they perform these functions in different ways - synchronously and asynchronously.

How do you mix contract synchronization and asynchronous code while avoiding common error scenarios?

What about a user who expects syncSig to start async, but instead performs synchronization?

What about a user who could optimize syncSig by running async, but not longer, because he assumes he is already running async?

+1
c # asynchronous interface
Dec 30 '16 at 18:37
source share
2 answers

However, none of the answers relate to a specific scenario in which you should mix asynchronous and synchronizing code, and how to avoid common errors that arise because of this.

This is because it is a bad idea. Any mixing code must be completely temporary in nature, existing only during the transition to asynchronous APIs.

However, I wrote the entire article on this subject .

How do you mix contract synchronization and asynchronous code while avoiding common error scenarios?

As I describe in my post on asynchronous interfaces , async is an implementation detail. The task return method may end synchronously. However, I would recommend avoiding blocking implementations; if for some reason they cannot be avoided, then I carefully document that they exist.

Alternatively, you can use Task.Run as an implementation; I do not recommend this (for the reasons described in detail in my blog ), but this is an option if you must have a lock implementation.

What about a user who expects syncSig to start async, but instead performs synchronization?

The calling thread is blocked synchronously. This is usually a problem only for user interface threads - see below.

What about a user who could optimize syncSig by running async, but not longer, because he assumes he is already running async?

If the calling code is ASP.NET, it must call it directly and await . Synchronous code will be executed synchronously, which is desirable on this platform.

If the calling code is a UI application, then he needs to know that there are synchronous implementations, and can call it wrapped in Task.Run . This avoids blocking the user interface thread, regardless of whether the implementation is synchronous or asynchronous.

+1
Dec 31 '16 at 13:06
source share

Is this the right way to mix async and sync?

Mostly yes. If you have a common interface where some implementations can provide a synchronous implementation, while others only asynchronous, then returning a Task<T> makes sense. A simple return Task.FromResult may be appropriate. But there are exceptions, see below.

What about a user who expects syncSig to start async, but instead performs synchronization?

There are some aspects of interfaces that cannot be expressed in code.

If you require IContract so that it does not block the calling thread more than milliseconds before the Task returns (this is not a reasonable hard requirement in this exact form, but you get the basic idea), and it blocks the thread in any case that the contract violation and the user must Report this as an error for those who have implemented syncSig.

If your IContract requires that it not throw synchronous exceptions, that any errors are reported using Task.FromException or its equivalent, and your syncSig throws a synchronous exception anyway, this is also a contract violation.

In addition, if syncSig just immediately returns the correct result, there is no reason why this should bother any user.

What about a user who could optimize syncSig by running async, but not longer, because he assumes he is already running async?

If synchronization SyncSig synchronously does not cause any problems, it does not matter, it still does not need to be optimized.

If a sync synchronous synchronous call causes problems, the basic debugging tools should soon tell the developer that syncSig is causing problems and needs to be investigated.

+3
Dec 30 '16 at 18:54
source share



All Articles