Await new Task<T>( ... ) : Task does not run?

10,388

Solution 1

To create a Task already started

Try creating the task like this:

Task.Factory.StartNew<object>((Func<Task<object>>) ...);

To create a Task without starting it

If you don't want the task started, just use new Task<object>(...) as you were using, but then you need to call Start() method on that task before awaiting it!

[Reference]

My recommendation

Just make a method that returns the anonymous function, like this:

private static Func<object> GetFunction( ) {
    return (Func<object>)(( ) => {
        SimpleMessage.Show( "TEST" );
        return new object( );
    } );
}

Then get it and run it in a new Task whenever you need it (Also notice that I removed the async/await from the lambda expression, since you are putting it into a Task already):

Task.Factory.StartNew<object>(GetFunction());

One advantage to this is that you can also call it without putting it into a Task:

GetFunction()();

Solution 2

You should never use the Task constructor (or Task.Start).

I do not want this function to return a task that is already running ( that is IMPERATIVE ).

So, you want to return some code that won't execute until you call it? That's a delegate, not a Task.

private static Func<Task<object>> GetInstance()
{
  return async () =>
  {
    await SimpleMessage.ShowAsync("TEST");
    return new object();
  };
}

When you're ready to execute it, call it just like any other delegate. Note that it returns a Task, so you can await the result:

var func = GetInstance();
// Delegate has not started executing yet
var task = func();
// Delegate has started executing
var result = await task;
// Delegate is done

Solution 3

You are stuck with a bad design there. I'll try to make something work for you under these constraints.

The only way to delay start tasks is to use the Task.Start method. (You also can use RunSynchronously but that doesn't really make use of any of the tasks features. At this point the task becomes an unthreaded lazy object.

So use the Task.Start method.

await does not start tasks. It waits for tasks that already run. Therefore await new Task(() => { }) always freezes forever.

Another problem here:

return new Task<object>( (Func<Task<object>>)(async ( ) => {
    await SimpleMessage.ShowAsync( "TEST" );
    return new object( );
} ) );

When you start that task it will complete nearly instantly and Task.Result will hold another task - the one returned by the async lambda. I doubt this is what you want. Hard to make this work and I don't know what you need.

This smells like the XY problem. Can you elaborate on what you want to accomplish? It feels like you have given yourself unnecessary constraints.

Share:
10,388
Will
Author by

Will

Updated on June 05, 2022

Comments

  • Will
    Will almost 2 years

    A continuation of a question asked here :

    In the aforementioned question I have the following function which returns an object of type Task (for incremental testing purposes) :

    private static Task<object> GetInstance( ) {
        return new Task<object>( (Func<Task<object>>)(async ( ) => {
            await SimpleMessage.ShowAsync( "TEST" );
            return new object( );
        } ) );
    }
    

    When I call await GetInstance( );, the function is called (and I assume the task is returned since no exception is thrown) but then the task just sits there.

    I can only guess I am doing this wrong then.

    I do not want this function to return a task that is already running ( that is IMPERATIVE ).

    How do I asynchronously run the task returned by this function?

  • Will
    Will over 8 years
    Wait - Won't that START the task? I need to return the task without it starting, and then start it again. I thought I was pretty specific about that (If it doesn't start the task then it's fine but this looks like it returns a task that's already running to me...)
  • Will
    Will over 8 years
    I think it would be better taken into chat - No need to transcribe war and peace here... now I just need to figure out how to start a chat session without being prompted to do so...
  • Lukazoid
    Lukazoid over 8 years
    Why would you use dynamic when you know the type?
  • Will
    Will over 8 years
    Thank you - but there is more to this than what you think. I need the task because I need it to support task cancellation and restarting. I did not want to bog down the question with what I felt to be details not pertinent to the immediate question.
  • Stephen Cleary
    Stephen Cleary over 8 years
    @Will: This can work fine with cancellation. What exactly do you mean by "restarting"?
  • Incerteza
    Incerteza over 8 years
    Why should one never use Task constructor or Start?
  • Stephen Cleary
    Stephen Cleary over 8 years
    @アレックス: I explain in full on my blog (constructor and Start).
  • Will
    Will over 8 years
    @StephenCleary : I mean that there is a long-running task in play which executes functions in the background. The task should be one that can be cancelled and started again with relative impunity. Using a Task seemed like the natural choice (given the presence of the CancellationTokenSource class (or whatever it's called I'm too tired to look it up...).
  • Will
    Will over 8 years
    Saw your edit : I like that. It looks good... Once I get out of the spiders web I'll have to try it...
  • Stephen Cleary
    Stephen Cleary over 8 years
    @Will: CancellationToken works fine with any kind of code. Restarting tasks is not possible; tasks have to be recreated. If you take the Func<Task> approach, you can create a replacement task by just calling the delegate again.
  • Will
    Will over 8 years
    @StephenCleary : I know that. The problem into which I was running (again; a whole lot of stuff going on beyond what I posted) was that if the user acted... well, like a USER, you would end up with multiple tasks running right next to each other. Train wreck. When I say "Restart" I mean exactly what you say - get a new task and run it... My solution to the "Train Wreck" is to obtain the task and monitor it. I have to monitor the status when the button responsible for launching (and cancelling) it is pressed because cancelling the task isn't instantaneous. The task runs until it hits CT.Throw
  • Will
    Will over 8 years
    @StephenCleary (Continued) : and as users can just hammer away at the button you could end up with (like I said) a train wreck in which multiple tasks are running towards cancellation at the same time. Rather than just let the user mash the button it has to watch the status of the task. Is it just created? Good : run it. No? Has cancel been called on the cancellation token? No? Fine : Cancel it. Has that happened? Okay : Scream at the user to stop being stupid; once the task has been cancelled (correctly) then the CTS will be disposed of and restored so that the process can start again.
  • Will
    Will over 8 years
    DING There's been a development... chat.stackoverflow.com/rooms/97243/await-new-taskt
  • Will
    Will over 8 years
  • Stephen Cleary
    Stephen Cleary over 8 years
    @Will: Just use a Lazy<Task> then, with a CTS.IsCancellationRequested check.
  • Will
    Will over 8 years
    I've already gotten where I need to be (unless what you are suggesting would resolve my new problem here : stackoverflow.com/questions/34147144/… ); Thanks.
  • The Sharp Ninja
    The Sharp Ninja over 8 years
    For some reason the compiler would not recognize the nested Task<object>. Using dynamic allowed it to skip the compiler and at run time get to the second task result.
  • Lukazoid
    Lukazoid over 8 years
    That's because FooGet() is creating and returning a Task<object> and not Task<Task<object>> (which is what it actually is).
  • Joanvo
    Joanvo over 8 years
    Of course, I assumed that was what you wanted because you were awaiting it. Otherwise you should call Task.Start()?
  • Joanvo
    Joanvo over 8 years
    I modified my answer, hope it helps
  • Will
    Will over 8 years
    Given the complexity of the situation with which I am dealing this question is out of the scope of consideration (TL;DR : I was doing it wrong). However your answer is the correct approach for creating a task and returning it without starting it.