Reputation: 2370
I am trying to create a system that processes events as they come in as separate Tasks. This is all inside a Windows Service application that runs until it is stopped, and when stopped, waits until all Tasks complete before stopping.
I have the following, simplified, code:
IList<Task> _tasks = new List<Task>();//this is a private class variable.
private void Event_Fired(object sender, eventArgs e)
{
StartTask();
}
private void StartTask(){
Task task = Task.Factory.StartNew(async () => await process.ProcessEvent(true, StoppingToken));
_tasks.Add(task);
}
private void MonitorTasks(){
for (int i = _tasks.Count - 1; i >= 0; i--)
{
Task aTask = _laneTasks[i];
if (aTask.IsCompleted)
{
_tasks.Remove(aTask);
Log.Information($"Task {aTask.Id} completed.");
}
}
}
public async Task ProcessEvent(){
//do some async processing
await Task.Delay(5000); //if I add a delay before returning the Task, it is completed prior to this delay.
}
The issue I'm having is that the Task is completing before ProcessEvent has completed. I need to ensure the tasks are completed before allowing the process to stop.
The process also has to allow new events to enter and spawn new Tasks at anytime.
I feel like I'm missing something fundamental here. Any help would be appreciated.
Upvotes: 0
Views: 532
Reputation: 16574
Task.Factory.StartNew(...)
creates a new Task
that executes the supplied delegate. The concrete type of the returned Task
will depend on the return type of the delegate.
So let's look at the delegate you're passing in:
async () => await process.ProcessEvent(true, StoppingToken)
OK, that's a Func<Task>
. Invoking the function will create a new Task
and return it. So your Task<Task>
completes immediately, with the Result
value set to the inner Task
object.
To complicate matters a little further, ProcessEvent()
itself creates a Task
that is, ultimately, what you want to wait on. So you're creating a Task
to create a Task
to wait on a Task
. Of course the outer-most Task
finishes as soon as the waiter Task
is created, since its job is now done.
Since your code already does manual waiting across a collection of Task
objects, you don't need a Task
whose whole purpose is to wait for the innermost Task
to complete. And since you're already returning a Task
from ProcessEvent()
you don't need a Task
to create a Task
.
Do this instead:
_tasks.Add(ProcessEvent());
Upvotes: 3