Resource locking with async/await

20,508

Solution 1

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.

Solution 2

There's SemaphoreSlim.WaitAsync which fits closely here. (I found it in a similar question).

Share:
20,508
Dan Bryant
Author by

Dan Bryant

I work with Positronics, a small consulting firm that develops custom software solutions for industrial automation. Or, as I like to tell my friends, "I get paid to play with robots." My specializations are machine vision and .NET architecture, with a touch of ladder logic every now and then.

Updated on August 03, 2020

Comments

  • Dan Bryant
    Dan Bryant over 3 years

    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.