I want to implement a class or pattern that ensures that I never execute more than one Task at a time for a certain set of operations (HTTP calls). The invocations of the Tasks can come from different threads at random times. I want to make use of the async-await pattern so that the caller can handle exceptions by wrapping the call in a try-catch.
Here's an illustration of the intended flow of execution:
Pseudo code from caller:
try {
Task someTask = GetTask();
await SomeScheduler.ThrottledRun(someTask);
}
catch(Exception ex) {
// Handle exception
}
The Task
class here might instead be an Action
class depending on the solution.
Note that I when I use the word "Schedule" in this question I'm not necessarily using it with relation to the .NET Task Scheduler. I don't know the async-await library well enough to know at what angle and with what tools to approach this problem. The TaskScheduler might be relevant here, and it may not. I've read the TAP pattern document and found patterns that almost solve this problem, but not quite (the chapter on interleaving).
There is a new ConcurrentExclusiveSchedulerPair
type in .NET 4.5 (I don't remember if it was included in the Async CTP), and you can use its ExclusiveScheduler
to restrict execution to one Task
at a time.
Consider structuring your problem as a Dataflow. It's easy to just pass a TaskScheduler
into the block options for the parts of the dataflow you want restricted.
If you don't want to (or can't) use Dataflow, you can do something similar yourself. Remember that in TAP, you always return started tasks, so you don't have the "creation" separated from the "scheduling" like you do in TPL.
You can use ConcurrentExclusiveSchedulerPair
to schedule Action
s (or async
lambdas without return values) like this:
public static ConcurrentExclusiveSchedulerPair schedulerPair =
new ConcurrentExclusiveSchedulerPair();
public static TaskFactory exclusiveTaskFactory =
new TaskFactory(schedulerPair.ExclusiveScheduler);
...
public static Task RunExclusively(Action action)
{
return exclusiveTaskFactory.StartNew(action);
}
public static Task RunExclusively(Func<Task> action)
{
return exclusiveTaskFactory.StartNew(action).Unwrap();
}
There are a few things to note about this:
ConcurrentExclusiveSchedulerPair
only coordinates Task
s that are queued to its schedulers. A second instance of ConcurrentExclusiveSchedulerPair
would be independent from the first, so you have to ensure the same instance is used in all parts of your system you want coordinated.async
method will - by default - resume on the same TaskScheduler
that started it. So this means if one async
method calls another async
method, the "child" method will "inherit" the parent's TaskScheduler
. Any async
method may opt out of continuing on its TaskScheduler
by using ConfigureAwait(false)
(in that case, it continues directly on the thread pool).If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With