Skip to content

Instantly share code, notes, and snippets.

@clemensv
Last active April 6, 2018 03:16
Show Gist options
  • Save clemensv/6554605 to your computer and use it in GitHub Desktop.
Save clemensv/6554605 to your computer and use it in GitHub Desktop.
using Task.WhenAny to drive a pump of multiple async operations in the same context (such as an I/O pump)
/*
Example use (from an email I sent)
I’m using WhenAny to either register for a callback on any of the pending operations and to use
the IO thread that comes up with their respective completion or to execute handling of completed
work on the current thread (‘borrowing’ the IO thread).
So if I read from a socket and from a queue, first both ops hang. Queue read completes and I get
called back on an IO thread which continues the await. I process that completion in the loop.
Meanwhile the socket read completes. That IO thread pops up and just registers the fact that it’s
done.
I get done dealing with the queue work and pop back to the top of the loop and schedule a new
receive on the queue. Then I land in await WhenAny, which promptly continues as the socket operation
sits there, completed, and I’m still on the IO thread I got from the queue.
I process the socket completion, pop back to the top of the loop, schedule a new socket receive
and run into await WhenAny, which has no compilations left and thus registers for a completion
callback with what’s pending and then unwinds the stack to return the borrowed IO thread to the pool.
*/
using System;
using System.Threading.Tasks;
namespace WhenAnyPattern
{
class Program
{
static void Main(string[] args)
{
MainAsync(args).Wait();
}
static async Task MainAsync(string[] args)
{
var rnd = new Random();
Task op1 = null;
Task op2 = null;
Task op3 = null;
var doneHere = new TaskCompletionSource<bool>();
Task.Delay(60000).ContinueWith(obj => doneHere.SetResult(true));
do
{
// receive from the queue
op1 = op1 ?? Task.Delay(rnd.Next(1000));
// read from the socket
op2 = op2 ?? Task.Delay(rnd.Next(1000));
// ping delay
op3 = op3 ?? Task.Delay(rnd.Next(1000));
// wait for any of the four operations (including completion) to be done
var completedTask = await Task.WhenAny(op1, op2, op3, doneHere.Task);
if (completedTask == op1)
{
try
{
Console.WriteLine("1");
}
finally
{
op1 = null;
}
}
else if (completedTask == op2)
{
try
{
Console.WriteLine("2");
}
finally
{
op2 = null;
}
}
else if (completedTask == op3)
{
try
{
Console.WriteLine("3");
}
finally
{
op3 = null;
}
}
else
{
// closing event was fired
break;
}
}
while (true);
Console.WriteLine("Done");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment