I am trying to create a Task pipeline/ordered scheduler in combination with using TaskFactory.FromAsync
.
I want to be able to fire off web service requests (using FromAsync
to use I/O completion ports) but maintain their order and only have a single one executing at any one time.
At the moment I do not use FromAsync
so I can do TaskFactory.StartNew(()=>api.DoSyncWebServiceCall())
and rely on the OrderedTaskScheduler
used by the TaskFactory
to ensure that only one request is outstanding.
I assumed that this behaviour would stay when using the FromAsync
method but it does not:
TaskFactory<Stuff> taskFactory = new TaskFactory<Stuff>(new OrderedTaskScheduler());
var t1 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
var t2 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
var t3 = taskFactory.FromAsync((a, s) => api.beginGetStuff(a, s), a => api.endGetStuff(a));
All of these beginGetStuff
methods get called within the FromAsync
call (so although they are dispatched in order but there are n
api calls occurring at the same time).
There is an overload of FromAsync
that takes a TaskScheduler:
public Task FromAsync(
IAsyncResult asyncResult,
Action<IAsyncResult> endMethod,
TaskCreationOptions creationOptions,
TaskScheduler scheduler
)
but the docs say:
The TaskScheduler that is used to schedule the task that executes the end method.
And as you can see, it takes the already constructed IAsyncResult
, not a Func<IAsyncResult>
.
Does this call for a custom FromAsync
method or am I missing something? Can anyone suggest where to start on this implementation?
Cheers,
EDIT:
I want to abstract this behaviour away from the caller so, as per the behaviour of TaskFactory
(with a specialized TaskScheduler
), I need the Task to be returned immediately - this Task will not only encapsulate the FromAsync
Task but also the queueing of that task whilst it awaits its turn to execute.
One possible solution:
class TaskExecutionQueue
{
private readonly OrderedTaskScheduler _orderedTaskScheduler;
private readonly TaskFactory _taskFactory;
public TaskExecutionQueue(OrderedTaskScheduler orderedTaskScheduler)
{
_orderedTaskScheduler = orderedTaskScheduler;
_taskFactory = new TaskFactory(orderedTaskScheduler);
}
public Task<TResult> QueueTask<TResult>(Func<Task<TResult>> taskGenerator)
{
return _taskFactory.StartNew(taskGenerator).Unwrap();
}
}
However, this utilizes a thread whilst the FromAsync
call is occurring. Ideally I'd not have to do that.
You cannot schedule IO tasks because they do not have a thread associated with them. The Windows Kernel provides thread-less IO operations. Starting these IOs does not involve managed code and the TaskScheduler
class does not come into play.
So you have to delay starting the IO until you are sure you actually want the network to be hit. You could use SemaphoreSlim.WaitAsync
to throttle the amount of tasks currently running. Await the result of that method before starting the individual IO and awaiting that as well.
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