Start May Not Be Called on a Promise-Style Task. Exception Is Coming

Start may not be called on a promise-style task. exception is coming

You are getting that error because the Task class already started the task before giving it to you. You should only ever call Start on a task that you create by calling its constructor, and you shouldn't even do that unless you have a compelling reason to not start the task when you create it; if you want it started right away you should use Task.Run or Task.Factory.StartNew to both create and start a new Task.

So, now we know to just get rid of that pesky Start. You'll run your code and find that the message box is shown right away, not 5 seconds later, what's up with that?

Well, Task.Delay just gives you a task that will be completed in 5 seconds. It doesn't stop execution of the thread for 5 seconds. What you want to do is have some code that's executed after that task finishes. That's what ContinueWith is for. It lets you run some code after a given task is done:

public void FunctionA()
{
Task.Delay(5000)
.ContinueWith(t =>
{
MessageBox.Show("Waiting Complete");
});
}

This will behave as expected.

We could also leverage C# 5.0's await keyword to add continuations more easily:

public async Task FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}

While a full explanation of what's going on here is beyond the scope of this question, the end result is a method that behaves very similar to the previous method; it will show a message box 5 seconds after you call the method, but the method itself will return [almost] right away in both cases. That said, await is very powerful, and allows us to write methods that seem simple and straightforward, but that would be much harder and messier to write using ContinueWith directly. It also greatly simplifies dealing with error handling, taking out a lot of boilerplate code.

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

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).

Getting error on foo.Start() when using Task.WhenAll for multiple tasks

        await Task.WhenAll(
dashboardDailyAgeAnalysis, dashboardDailyBalance, dashboardDailyInventory
);

// Do something with these results.
var result1 = await dashboardDailyAgeAnalysis;
var result2 = await dashboardDailyBalance;
var result3 = await dashboardDailyInventory;

How to compose a unit test if a task is followed by task.start()?

The Task.Start method can only be called on "cold" tasks, in other words on tasks that have not been started yet. Such tasks can only be created with the Task constructor. Tasks created by invoking asynchronous methods implemented with the async keyword are "hot", in other words they are already started upon creation. From the docs:

Exceptions

InvalidOperationException

The Task is not in a valid state to be started. It may have already been started, executed, or canceled, or it may have been created in a manner that doesn't support direct scheduling.

This is also a good reading: A Tour of Task, Part 10: Promise Tasks

C# starting tasks to run async methods

How can I start a task to await an async action?

In the TAP, tasks are returned "hot". So you literally just need to call the method and keep the task it returns:

private ConcurrentDictionary<int, Task> Tasks { get; set; }
private async Task StartTasks()
{
for(int i = 0; i < 5; i++)
{
Task task = DoWork(null);
Tasks.TryAdd(task.Id, task);
}
}

I cannot use Task.Run() since that does not provide TaskCreationOptions. These tasks will run for literal days.

You don't need TaskCreationOptions. The LongRunning flag is just a hint, and the thread pool will recover within a couple of seconds even if you don't specify that hint. But that doesn't even matter, because your work is asynchronous so it doesn't need to run on the thread pool in the first place.

TPL Producer consumer pattern - Start may not be called on a task that was already started

Remove the call to Start(). Since StartNew() starts the task it creates, there's no need for the Start().

Executing Tasks in Parallel - Silverlight

"Start may not be called on a promise-style task." is a somewhat misleading message for the simple thing: the task has already been started.

Thus, you may just omit the first loop.

Silverlight does not have Task.WhenAll, but if you are using Microsoft.Bcl.Async, it contains TaskEx.WhenAll which is the same.

await TaskEx.WhenAll(displayThumbnailTasks);

Execute parallel tasks with async/await



Related Topics



Leave a reply



Submit