简体   繁体   中英

How to make sure a task is started and safely start it if not?

I get an IEnumerable<Task> tasks from somewhere that I do not control. I don't know if the tasks are manually created using new Task , Task.Run , or if they are a result of an async method call async Task DoSomethingAsync() .

If I do await Task.WhenAll(tasks) , I risk hanging indefinitely because maybe one or more of the tasks are not started.

I can't do tasks.ForEach(t => t.Start()) , because then I will get an InvalidOperationException "Start may not be called on a promise-style task" if it's from an async method call (already started).

I can't do await Task.WhenAll(tasks.Select(t => Task.Run(async () => await t))) because each t still does not start just by awaiting it.

I assume the solution has something to do with checking each task's Status and Start() based on that, but I also assume that it can be tricky because that status could change at any time, right? If this is still the way to go, which statuses would be correct to check and what threading issues should I worry about?

Non working case example:

//making an IEnumerable as an example, remember I don't control this part
Task t = new Task( () => Console.WriteLine("started"));
IEnumerable<Task> tasks = new[] {t};

//here I receive the tasks
await Task.WhenAll(tasks);//waits forever because t is not started

Working case example:

//calls the async function, starting it.
Task t = DoSomethingAsync();
IEnumerable<Task> tasks = new[] {t};

//here I receive the tasks and it will complete because the task is already started
await Task.WhenAll(tasks);

async Task DoSomethingAsync() => Console.WriteLine("started");

If for whatever reason you cannot change the code to not return unstarted tasks, you can check Status and start task if it has Created status:

if (task.Status == TaskStatus.Created)
   task.Start();

All other task statues indicate that task is either completed, running, or being scheduled, so you don't need to start tasks in that statuses.

Of course in theory this introduces race condition, because task can be started right between your check and Start call, but, as correctly pointed by Servy in comments - if there ever is race condition here - that means another party (which created that task) is also trying to start it. Even if you handle exception ( InvalidOperationException ) - another party is unlikely to do that, and so will get exception while trying to start their own task. So only one side (either you, or code that created that task) should be trying to start it.

That said - much better than doing this is to ensure you might never get unstarted task in the first place, because it's just bad design to return such tasks to external code, at least without explicitly indicating that (while it's for some use cases ok to use unstarted task internally).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM