Reputation: 141
I have two asynchronous methods (let's call them AAsync()
and BAsync()
) that are intended to run one after the other one. This is because method AAsync()
prepares the context for method BAsync()
to act properly. For each call of AAsync()
there's a correspondent call of the method BAsync()
.
These methods can be fired in the following order (as an example):
A1A2B1B2A3B3B4A4, where A stands for AAsync
, B for BAsync
, and the subindex indicates the call number of the method.
Given that it will always be that method AAsynci will be called after method AAsynci-1 and before AAsynci+1 (same applies for BAsync), and that a call AAsynci has a correspondant call BAsynci, I would like to know which is the best way to, given a disordered firing of the methods (like the one in the example above), execute the calls in the following order:
A1B1A2B2A3B3A4B4 ...
I have solved this using two FIFO Semaphores and two TaskCompletionSource
(see below), but I have the feeling that this can be solved in a smarter way. Does anybody know a nicer solution?
TaskCompletionSource TCS_A;
TaskCompletionSource TCS_B;
FIFOSemaphore MutexA = new FIFOSemaphore(1);
FIFOSemaphore MutexB = new FIFOSemaphore(1);
async void AAsync()
{
await MutexA.WaitAsync();
if (TCS_B!= null)
await TCS_B.Task;
TCS_B = new TaskCompletionSource<bool>();
// Do intended stuff...
TCS_A?.TrySetResult(true);
MutexA.Release();
}
async void BAsync()
{
await MutexB.WaitAsync();
if (TCS_A!= null)
await TCS_A.Task;
TCS_A = new TaskCompletionSource<bool>();
// Do intended stuff...
TCS_B?.TrySetResult(true);
MutexB.Release();
}
Upvotes: 1
Views: 80
Reputation: 43525
My suggestion is to use two Channel<T>
instances. The channel A should receive the arguments passed to the A event handler, and the channel B should receive the arguments passed to the B event handler. Finally perform an asynchronous loop that takes one item from the channel A and one from the channel B ad infinitum:
Channel<EventArgs> _channelA = Channel.CreateUnbounded<EventArgs>();
Channel<EventArgs> _channelB = Channel.CreateUnbounded<EventArgs>();
void EventA_Handler(EventArgs e)
{
_channelA.Writer.TryWrite(e);
}
void EventB_Handler(EventArgs e)
{
_channelB.Writer.TryWrite(e);
}
async void EventHandlerLoop()
{
while (true)
{
EventArgs a = await _channelA.Reader.ReadAsync();
// Do intended stuff...
EventArgs b = await _channelB.Reader.ReadAsync();
// Do intended stuff...
}
}
The method EventHandlerLoop
should be invoked during the initialization of the class, to start the wheel rolling.
If you are not familiar with the channels, you could take a look at this tutorial.
Upvotes: 1