In this scenario, system A needs to send a message to system B. The following code shows a sample of how this was accomplished:
public interface IExecutionStrategy
{
Task<Result> ExecuteMessage(Message message);
}
public class WcfExecutionStrategy
{
public async Task<Result> ExecuteMessage(Message message)
{
using (var client = new Client())
{
return await client.RunMessageOnServer(message);
}
}
}
public class MessageExecutor
{
private readonly IExecutionStrategy _strategy;
public MessageExecutor(IExecutionStrategy strategy)
{
_strategy = strategy;
}
public Task<Result> ExecuteMessage(Message msg)
{
// ....
// Do some common checks and code here
// ....
var result = await _strategy.ExecuteMessage(msg);
// ....
// Do some common cleanup and logging here
// .....
return result;
}
}
For reasons out of scope of this question we decided to switch from Wcf to using raw http streams, but we needed both side by side to gather metrics and test it out. So I created a new IExecutionStrategy
implementation to handle this:
public class HttpclientExecutionStrategy
{
public async Task<Result> ExecuteMessage(Message message)
{
var request = CreateWebRequestmessage
var responseStream = await Task.Run(() =>
{
var webResponse = (HttpWebResponse)webRequest.GetResponse();
return webResponse.GetResponseStream();
}
return MessageStreamer.ReadResultFromStream(responseStream);
}
}
Essentially, the only way I could get this to be asynchronous was to wrap it in a Task.Run()
so the web request was none blocking. (Note: due to unique stream manipulation requirements on both sending and receiving it is not straight forward to implement this is in HttpClient
, and even if it's possible this fact is out of scope for this question).
We thought this was fine until we read Stephen Cleary's multiple blog posts about how Task.Run()
is bad, both in library code and in Asp.Net applications. This makes perfect sense to me.
What doesn't make sense is how you actually implement a naturally asynchronous call if the third party library does not support an asynchronous movement. For example, if you were to use HttpClient.GetStreamAsync()
what does that do that makes it better for asynchronous operations than Task.Run(() => HttpClient.GetStream())
, and is there any way to remedy this for non-async third party libraries?
You can use await Task. Yield(); in an asynchronous method to force the method to complete asynchronously. Insert it at beginning of your method and it will then return immediately to the caller and complete the rest of the method on another thread.
There's no rule that says that "asynchronous cannot call asynchronous". There are specific rules in place, such as "future cannot call future". A Queueable can call another Queueable, a Batchable can call another Batchable in the finish method, and Scheduleable methods can call Batchable and Queueable methods.
The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.
Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object.
HttpClient.GetStreamAsync
is a pure asynchronous method, which means no new threads will be introduced while making the call, and when using in combination with await
, will yield control back to the caller until the IO request is done. This will scale well, as you actually free the ThreadPool thread that invoked the operation to so more work while the request is executing, so your server can actually process more requests in the meantime.
On the contrary, using a dedicated thread (sync over async) just to make a blocking IO request call will definitely not scale well, and might eventually cause a starvation if the execution time is long enough.
Edit
The truely asynchronous nature of the XXXAsync
implementation comes from the network device driver supplying an asynchronous endpoint to the OS. Under the covers the WinHTTP
(Thanks @Noseratio for the correction) library is used for the async operations. What that means is that an I/O Request Packet (IRP) is generated and passed to the device driver. Once the request is complete, a CPU interrupt will occur which will eventually cause the callback registered to be invoked. You can look at theses examples: Using WinInet HTTP functions in Full Asynchronous Mode or Windows with C++ - Asynchronous WinHTTP for asynchronous examples, and of course read the excellent There Is No Thread by Stephan Cleary. You can natively implement it yourself and wrap it in a managed wrapper.
Use async-await
if the operation is truly asynchronous. If it's not, then don't pretend it is.
Asynchronous operations have only 2 real benefits: scalability and offloading. Scalability is mostly for the server-side while offloading is used for "special" threads (mostly GUI
threads).
If you're dependent on a synchronous library and there's nothing you can do about, making it asynchronous using Task.Run
doesn't improve scalability at all (and may in fact hinder it), and you're only left with offloading. async
reduces resource usage only when the operation is inherently asynchronous, when there is no thread throughout the actual asynchronous part (network, disk, etc.)
If you're developing a rich-client application, go ahead and use "sync over async" with async-await
. But for any other type of application, there's no reason to use async-await
, and I would recommend against it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With