3

Is it possible to queue a new task for execution when all currently scheduled tasks have finished? I cannot use TaskFactory.ContinueWhenAll because I don't have references to the scheduled tasks as they are scheduled by subcomponents. Getting all currently scheduled tasks would help as in that case I could use the ContinueWhenAll method using them.

Bartosz Wójtowicz
  • 1,321
  • 10
  • 18
  • 3
    You could probably implement your own TaskScheduler which is a wrapper around the actual TaskScheduler. – i3arnon Jul 04 '14 at 09:15
  • I don't see how this could be done as GetScheduledTasks() is a protected method. – Bartosz Wójtowicz Jul 04 '14 at 09:26
  • Simply have your own TaskScheduler that stores every queued task in a list before passing it onward to the "real" TaskScheduler – i3arnon Jul 04 '14 at 09:31
  • I see, however the other tasks are created with the default scheduler. I would have to swap the schedulers for the task factory. Is that possible? – Bartosz Wójtowicz Jul 04 '14 at 09:39
  • 1
    As long as they don't specifically use TaskScheduler.Default, yes. see this answer: http://stackoverflow.com/a/21968669/885318 – i3arnon Jul 04 '14 at 09:43
  • I understand from the answer that the subcomponents would have to use my scheduler when launching the tasks. The problem is I can't change that code. I suppose they launch the tasks like this: Task.Factory.StartNew(() => DoAction); – Bartosz Wójtowicz Jul 04 '14 at 10:01
  • 1
    You do not have control over what tasks are running in your process. Any library, including the BCL, can start tasks. Waiting for all tasks is very risky and destabilizing. – usr Jul 04 '14 at 10:19

1 Answers1

3

Is it possible to queue a new task for execution when all currently scheduled tasks have finished?

I don't think there's a universal way of doing this, unless you want to resort to reflection hacks (which would inevitably bind you to the current TPL implementation details).

Say, you could implement a custom TaskScheduler and pass it to Task.Factory.StartNew, Task.ContinueWith or Task.Start. You'd still be unable to control the tasks created by others, which explicitly use TaskScheduler.Default or TaskScheduler.FromSynchronizationContext().

Besides, there may be tasks out there created by async methods, like this:

async Task DoAsync() { await Task.Delay(1); }

They are created without a task scheduler at all (in TaskScheduler sense). You could implement a custom synchronization context and track items posted with SynchronizationContext.Post. However not all of those items would necessarily represent task continuations. Morever, you won't be able to track continuations like await task.ConfigureAwait(false).

What may be a sign that you need to redesign your task workflow logic or coordinate it with other developers. Say, you have access to some root parent tasks which you want to track with TaskFactory.ContinueWhenAll. Those parent tasks create some child tasks in fire-and-forget manner and then complete, and the child tasks are unavailable to you. This would be wrong: the parent tasks should not normally complete until all child tasks have completed. There are at least several ways of fixing this, like using async/await or Task.Unwrap, provided the 3rd party code can be changed.

noseratio
  • 59,932
  • 34
  • 208
  • 486