How to refactor 'lock' to 'async/await'? - c#

We have an old library written in C# targeting framework 2.0. Recently we are going to use it in a modern .net core project and intend to use async/await. However, the old library has a lot of lock blocks.
We plan to add new async methods to implement the same logic.
For example,
the old code
void GetOrder()
{
// ...
lock(_lock)
{
//...
}
}
expected result
async Task AsyncGetOrder()
{
// ...
await DoSomethingWithLock()
}
Please give me some advices about how to translate lock into async/await.

You could use SemaphoreSlim, but if there's a lot of it, the AsyncLock library will probably make the conversion much easier (and cleaner).
Just go with the AsyncLock library and relax.

The first thing you need to take into account is that async methods can call non-async methods, but it's not trivial for non-async methods to call async methods and wait for them to finish.
This means that every method that has a lock inside of it will probably need to be called only by async methods. You can do blocking waits for async methods but then there's no point to refactoring and you have to be very careful to avoid deadlocks.
You also need to be aware that in some project types there's an importance to the identity of the executing thread. For example in WPF there are some things that only the UI thread is allowed to do, and if you launch a Task that runs such code from a thread in the thread-pool, you're likely to experience exceptions.
Having said that, if you want to refactor a method that waits on a lock into an async method that asynchronously waits for a lock/semaphore, you should use SemaphoreSlim and await the WaitAsync call. This way the async method will yield control when the SemaphoreSlim is blocking execution, and resume execution at some point later.

There are a few general approaches:
#1 Refactor away from the need for locks at all
Look around for how to write lock-free C#. Sometimes it involved judicious use of the Interlocked class. Other times it involves a shift in mindset toward immutable state (ask a functional programmer). There are many cases when doing this has boosted parallel performance significantly.
And of course lockless threadsafe code can be executed either synchronously or asynchronously.
#2 Refactor away from the need for a reentrant lock, then go async with something that doesn't allow reentrance
This is basically what people are recommending when they recommend SemaphoreSlim, the AsyncEx NuGet package, or similar.
Stephen Cleary has written about the wisdom behind going this route. He also gives some examples of how to do it:
https://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html
#3 Find an async drop-in replacement for Monitor.Lock/lock
Basically what you need is something that gives all three of these at the same time:
Asynchronicity
Reentrance
Mutual exclusion
Monitor.Lock/lock give you the second and third things. So you want something that gives you the first one also without sacrificing the other two.
This is a little trickier than it might seem. At first glance there are several NuGet packages which appear to do this. But the only correct one that I know of is this NuGet package (which I wrote):
https://www.nuget.org/packages/ReentrantAsyncLock/
Here it is in action:
var asyncLock = new ReentrantAsyncLock();
var raceCondition = 0;
// You can acquire the lock asynchronously
await using (await asyncLock.LockAsync(CancellationToken.None))
{
await Task.WhenAll(
Task.Run(async () =>
{
// The lock is reentrant
await using (await asyncLock.LockAsync(CancellationToken.None))
{
// The lock provides mutual exclusion
raceCondition++;
}
}),
Task.Run(async () =>
{
await using (await asyncLock.LockAsync(CancellationToken.None))
{
raceCondition++;
}
})
);
}
Assert.Equal(2, raceCondition);
This is certainly not the first attempt at doing this. But like I said it's the only correct attempt that I've seen so far. Some other implementations will deadlock trying to re-enter the lock in one of the
Task.Run calls. Others will not actually provide mutual exclusion and the
raceCondition variable will sometimes equal 1 instead of 2:
Stephen Cleary's POC does not provide mutual exclusion: https://dotnetfiddle.net/vLKyCX
NeoSmart.AsyncLock does not provide reentrance with mutual exclusion: https://dotnetfiddle.net/CkK674
Flettu sometimes does not provide reentrance, sometimes throws a semaphore count exception, or otherwise does not provide mutual exclusion: https://dotnetfiddle.net/o0c7j7
CellWars.Threading.AsyncLock does not provide mutual exclusion: https://dotnetfiddle.net/Tz38lN

Related

Handling synchronous methods in async/await best practices (turtles all the way down)

With async turtles all the way down ...
Some of the background research:
Async/Await - Best Practices in Asynchronous Programming
Async
In code I've seen:
Developers wrap code in a Task.Run() upon entering a method and awaiting it to force it to be async or (or older .NET 4.0 TaskFactory.StartNew())
I've personally just left it synchronous (but it partially violates turtles all the down as different branches often are sync)
I've also seen the attempt to use ValueTask to short-circuit the return as async given the nature of ValueTask with a return value being akin to:
return new ValueTask<int>(valueWantToReturn);
Example psudeo code:
public async Task ParentMethodAsync()
{
// ... do something upon entering
int neededValue = SyncNestedDryMethod(); // ** this part **
await _nthLayer.DoSomethingWithRepoAsync(); // ** same concept applies here
// if there is no asyncronous logic required **
// ... do anything else needed
}
public int SyncNestedDryMethod()
{
// ... do something that requires no real async method signature
// declaration because it is straight synchronous logic (covers reusing
// code for DRY purposes, method-level SRP abstraction (includes moving it
// to an abstracted class -- for future readers), etc.)
// I've seen making the method async then awaiting Task.Run() or
// Task.StartFactory.New() inside this (sync changed to async method)
// to try and force it to be async or last line being:
// return new ValueTask<int>(valueWantToReturn);
// but everything above it being only syncronous logic
}
Is there a best practice to be aware of not found addressed in the respective links (or overlooked) or elsewhere which I have not come across to address abstracting synchronous code into new methods (for DRY purposes, SRP, etc.) that do not have the ability to be async or are forced async all the while following async through and through?
Your question isn't entirely clear, but I think you're asking how to run synchronous code in an asynchronous way? If so, the answer is: you can't.
Using Task.Run() doesn't make the code asynchronous. It just runs the code on a different thread and gives you an asynchronous way to wait for that thread to complete. That may be what you need if you just need to get the work off of the current thread. For example, you would use that in a desktop application where you want to get the work off of the UI thread. But this wouldn't help you at all in ASP.NET.
ValueTask is used for cases where the work might be done asynchronously, but also may complete synchronously too. For example, if data is usually returned from an in memory cache, but refreshing that cache is done asynchronously. This is described in detail here. But if the work is always synchronous, then there is no point using ValueTask, or Task or async.

Running async functions in parallel from list of interfaces

I have a similair question to Running async methods in parallel in that I wish to run a number of functions from a list of functions in parallel.
I have noted in a number of comments online it is mentioned that if you have another await in your methods, Task.WhenAll() will not help as Async methods are not parallel.
I then went ahead and created a thread for each using function call with the below (the number of parallel functions will be small typically 1 to 5):
public interface IChannel
{
Task SendAsync(IMessage message);
}
public class SendingChannelCollection
{
protected List<IChannel> _channels = new List<IChannel>();
/* snip methods to add channels to list etc */
public async Task SendAsync(IMessage message)
{
var tasks = SendAll(message);
await Task.WhenAll(tasks.AsParallel().Select(async task => await task));
}
private IEnumerable<Task> SendAll(IMessage message)
{
foreach (var channel in _channels)
yield return channel.SendAsync(message, qos);
}
}
I would like to double check I am not doing anything horrendous with code smells or bugs as i get to grips with what I have patched together from what i have found online. Many thanks in advance.
Let's compare the behaviour of your line:
await Task.WhenAll(tasks.AsParallel().Select(async task => await task));
in contrast with:
await Task.WhenAll(tasks);
What are you delegating to PLINQ in the first case? Only the await operation, which does basically nothing - it invokes the async/await machinery to wait for one task. So you're setting up a PLINQ query that does all the heavy work of partitioning and merging the results of an operation that amounts to "do nothing until this task completes". I doubt that is what you want.
If you have another await in your methods, Task.WhenAll() will not help as Async methods are not parallel.
I couldn't find that in any of the answers to the linked questions, except for one comment under the question itself. I'd say that it's probably a misconception, stemming from the fact that async/await doesn't magically turn your code into concurrent code. But, assuming you're in an environment without a custom SynchronizationContext (so not an ASP or WPF app), continuations to async functions will be scheduled on the thread pool and possibly run in parallel. I'll delegate you to this answer to shed some light on that. That basically means that if your SendAsync looks something like this:
Task SendAsync(IMessage message)
{
// Synchronous initialization code.
await something;
// Continuation code.
}
Then:
The first part before await runs synchronously. If this part is heavyweight, you should introduce parallelism in SendAll so that the initialization code is run in parallel.
await works as usual, waiting for work to complete without using up any threads.
The continuation code will be scheduled on the thread pool, so if a few awaits finish up at the same time their continuations might be run in parallel if there's enough threads in the thread pool.
All of the above is assuming that await something actually awaits asynchronously. If there's a chance that await something completes synchronously, then the continuation code will also run synchronously.
Now there is a catch. In the question you linked one of the answers states:
Task.WhenAll() has a tendency to become unperformant with large scale/amount of tasks firing simultaneously - without moderation/throttling.
Now I don't know if that's true, since I weren't able to find any other source claiming that. I guess it's possible and in that case it might actually be beneficial to invoke PLINQ to deal with partitioning and throttling for you. However, you said you typically handle 1-5 functions, so you shouldn't worry about this.
So to summarize, parallelism is hard and the correct approach depends on how exactly your SendAsync method looks like. If it has heavyweight initialization code and that's what you want to parallelise, you should run all the calls to SendAsync in parallel. Otherwise, async/await will be implicitly using the thread pool anyway, so your call to PLINQ is redundant.

Wait() causes UI thread to hang - when should Wait() be used?

I have the following code that connects to a SignalR Hub
private static async Task StartListening()
{
try
{
var hubConnection = new HubConnection("http://localhost:8080/");
IHubProxy hubProxy = hubConnection.CreateHubProxy("Broadcaster");
hubProxy.On<EventData>("notifyCardAccessEvent", eventData =>
{
Log.Info(string.Format("Incoming data: {0} {1}", eventData.Id, eventData.DateTime));
});
ServicePointManager.DefaultConnectionLimit = 10;
await hubConnection.Start();
Log.Info("Connected");
}
catch (Exception ex)
{
Log.Error(ex);
}
}
In my Form_Load method, I have this
StartListening();
However, Resharper prompts me to "consider applying the 'await' operator to the result of the call"
So I did this:
Log.Info("Connecting to SignalR hub...");
StartListening().Wait();
Log.Info("Connected!");
However, this causes my UI thread to hang and Connected! is never printed to the log file.
So my question is, when should I use Wait()? What are the instances and scenarios that I should use Wait(), and when should I not use Wait()?
await is not Wait. It is unclear what the code is that is calling StartListening(), but one option is to await it, as suggested:
await StartListening();
However, in some other cases it may be better to do nothing at all:
StartListening(); // drop the Task on the floor
or perhaps use ContinueWith for a manual continuation. Since the StartListening method catches any exceptions, there isn't anything wrong with just ignoring the returned Task - so what you had already. I would suggest calling it StartListeningAsync, though.
The reason for the deadlock is that if you use Wait, your UI thread blocks waiting on an asynchronous method to complete, but that asynchronous method is capturing the sync-context, which means in order to process each continuation it tries to get onto the UI thread - which is blocked... on it.
#MarcGravell has the correct answer; I'm just going to answer this other question:
So my question is, when should I use Wait()? What are the instances and scenarios that I should use Wait(), and when should I not use Wait()?
The confusion is coming from the fact that the Task type is used for two almost completely different things.
Task was originally introduced in .NET 4.0 as part of the Task Parallel Library. Normally, you would use Parallel LINQ or the Parallel class for parallel processing (which used the Task type underneath). However, in advanced scenarios, you could use the Task type directly. Task.Wait was used to wait for those independent tasks to complete.
When async/await were introduced in .NET 4.5, the existing Task type was almost good enough to be used as an abstract "future". So instead of inventing some new "future" type, they just slightly extended Task to work as a future.
This brings us to today, where Task can be used as either:
An item of work in a parallel computation.
An asynchronous future.
(There's a tiny bit of crossover: you can treat parallel work as asynchronous, and in rare situations like Console Main methods you do need to block on asynchronous tasks; but ignore those for the moment.)
This means that the API for Task is split along those lines. Members such as Start, Wait, Result, and ContinueWith belong pretty firmly on the parallel side. In the asynchronous world, await is more appropriate.
I have a small table at the bottom of my async intro that has some new (asynchronous) equivalents for the old (parallel) ways of doing things.
You seem to misunderstand the message from Resharper. Instead of applying the await operator, you called the Task.Wait() Method. They may seem similar, but they're working completely different.
This nice answer will provide more information about the differences: https://stackoverflow.com/a/13140963/3465395

How do I fix this deadlock in Win Forms constructor when using async/await?

Here is my minimum repro case:
public Form1()
{
Task.Delay(100).Wait(); // Works just fine
this.Await().Wait(); // Blocks indefinitely
}
private async Task Await()
{
await Task.Delay(100);
}
What is going on here? Why are these two behaving differently? What can I do to make the later one work?
My actual case is less trivial, and I can't "just use the first option".
You're seeing a classic deadlock situation that I describe on my blog and in an MSDN article. In short, after the await completes, the async method is attempting to resume on the UI thread, which you have blocked by calling Wait.
To fix it, you ideally want to use async all the way (i.e., never block on async code). Constructors pose a difficulty here (since they cannot be async); I explore several options on my blog. The correct option depends on your code base, but I recommend the async factory method if possible. The options are:
Async factory method.
Asynchronous lazy initialization.
Asynchronous initialization pattern.
If you absolutely cannot use one of the options that I describe on my blog, then you can work around this by using ConfigureAwait(false) in all your async methods, and then your Wait() will not deadlock. However, this will block the UI thread during those asynchronous method calls (which sort of defeats the purpose of them being async in the first place...)

Does Task.RunSynchronously() work "recursively"?

If I call RunSynchronously() on a Task in C#, will this lead to asynchronous calls further down the rabbit hole to be run synchronously as well?
Let's say I have a method named UpdateAsync(). Inside this method another asynchronous call is made to DoSomethingAsync() and inside this again we find DoSomethingElseAsync(), will calling RunSynchronously() on 'UpdateAsync()' lead to RunSynchronously() also indirectly being called on DoSomethingAsync()?
The reason for my question:
I have a situation where I "need" to call an asynchronous method (UpdateAsync()) inside a catch-block and wonder if calling this with RunSynchronously() is safe. The documentation is quite clear on the fact that you can't await inside a catch-block. (Strictly speaking I could use a boolean inside the catch-block and call UpdateAsync() after the try-catch, but that feels rather dirty). Sorry about the dual question, but as you probably understand I don't quite know how to phrase it and do not have a really good understanding of this field.
(Edit:
I don't know how to find out if a method was called asynchronously. How would you write a unit test for this? Is it possible to log it somehow?)
I "need" to call an asynchronous method and wonder if calling this with RunSynchronously() is safe
There are two kinds of Tasks: code-based* Tasks (you can create those by using the Task constructor or Task.Factory.StartNew()) and promise-style Tasks (you can create them manually by using TaskCompletionSource or by writing an async method).
And the only Tasks that you can start by calling Start() or RunSynchronously() are unstarted code-based Tasks. Since async methods return promise-style Tasks (or possibly already started code-based Tasks), calling RunSynchronously() on them is not valid and will result in an exception.
So, to actually answer your question: what you're asking isn't possible, so it doesn't make sense to ask whether it's safe.
* This is not an official name, I don't know if there is one.
It's hard to predict without code how it will execute nested async methods.
You can log on each async method Thread.CurrentThread.ManagedThreadId property and compare id with other thread IDs that you have. When they vary, then your async methods are in multithreaded executtion
Or try to use Concurrency Visualizer from Visual Studio, Analyze menu. With Task class instances and even with C#5 async syntax there is no way to get to know that you are executing in parallel or another thread.
I think I'll be contradicting #svick's answer, but I feel the OP question is valid, because an async method's "promised" (to use Svick's terminology) Task can be obtained, but not started, thus allowing to do Task.RunSynchronously().
static void Main(string[] args)
{
...
//obtain a Task that's not started
var t = new Task<int>((ob) => GetIntAsync((string)ob).Result, someString);
...
}
static async Task<int> GetIntAsync(string callerThreadId)
{
...
Having said that, the answer is: No, the RunSynchronously() affects the task you run this on. If the call chain later contains more async calls, they run asynchronously.
I have a little console app that is modeling this, is someone is interested in seeing it, but the concept is pretty simple to reproduce - just chain enough asynchronous calls and toggle between running the earliest one synchronously and asynchronously to see the different behavior.

Categories