Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resource locking with async/await

I have an application where I have a shared resource (a Motion system) which can be accessed by multiple clients. I have individual Operations that require access to the system for the duration of the move and which should throw 'Busy' exceptions if conflicting operations are requested at the same time. I also have Sequencers which need to acquire exclusive access to the Motion system for the execution of several Operations, interspersed with other actions; during the entire sequence, no other clients should be able to run Operations.

I've traditionally approached this using Thread-affinity, so that a Thread can request exclusive access and run blocking calls corresponding to operations. While the Thread has access, no other Threads may use the resource. The problem I'm having now is that I've moved toward implementing my system using async/await patterns, to allow cleaner sequencer implementation. The problem is that now my sequencer is not always running on the same thread; the active thread can change during the course of callbacks, so it is no longer easy to determine whether I am in a valid context to keep running operations. One item of note is that some of the Operations themselves are composed of awaits, which means both sequences and individual Operations can span multiple threads.

My question: does anybody know of a good pattern to deal with acquiring exclusive access in the presence of thread switching due to async/await?

For reference, a few things I've considered:

  1. I could create a custom SynchronizationContext that marshals all sequencer calls for the duration of a sequence back to a single Thread. This has the benefit of allowing me to reuse my existing thread-affinity access management code. The downside is that this will require dedicating a Thread whenever I do either a Sequence or an Operation (since Operations can also span multiple threads.)

  2. Create an acquirable access token to pass to the Operation methods to prove that you have acquired access. This has the downside of bloating the methods with a token parameter.

  3. Use the access token approach from (2), but create a duplicate interface implementation for the Operations interface so a wrapper can be instantiated with the token 'baked-in'. This creates some ugly glue code, but it cleans up the sequencer code so that it no longer needs to pass a token to each method.

like image 601
Dan Bryant Avatar asked Oct 02 '12 16:10

Dan Bryant


People also ask

Can we use lock in async await?

The await keyword in C# (. NET Async CTP) is not allowed from within a lock statement. From MSDN: An await expression cannot be used in a synchronous function, in a query expression, in the catch or finally block of an exception handling statement, in the block of a lock statement, or in an unsafe context.

How can we avoid deadlock in async await?

Another solution is to call “ConfigureAwait(false)” on the task of the underlying method, to prevent the continuation of the task on the original context captured. If you really cannot use async all the way, then you could use “Task. Run(…)” to execute the async method in a separate thread.

Does await block the thread?

The await operator doesn't block the thread that evaluates the async method. When the await operator suspends the enclosing async method, the control returns to the caller of the method.

Does async await improve performance?

C# Language Async-Await Async/await will only improve performance if it allows the machine to do additional work.


1 Answers

My question: does anybody know of a good pattern to deal with acquiring exclusive access in the presence of thread switching due to async/await?

Yes, you can use AsyncLock, which is also available as part of my AsyncEx library. If you want to have a "TryLock" kind of operation, then you may have to create your own primitive.

You do lose some of the capability to do safety checks: there is no way to check whether the currently-executing thread has a specific AsyncLock.

Other options include ConcurrentExclusiveSchedulerPair (which I blog about here) or TPL Dataflow.

like image 157
Stephen Cleary Avatar answered Sep 16 '22 17:09

Stephen Cleary