0

I don't understand why the following code executes .ContinueWith() before Task.Delay() has ended its execution.

static void Main(string[] args)
{
    Task t = new Task(async () =>
    {
        Console.WriteLine("1 delaying");
        await Task.Delay(2000);
        Console.WriteLine("2 delayed");
    });

    t.Start();
    t.ContinueWith(_ => Console.WriteLine("3 continue"));

    Console.ReadKey();
}

Output is:

1 delaying 
3 continue 
2 delayed

But I was expecting that "3 continue" should be executed as last.

With Task.Run() it works as expected:

Task.Run(async () =>
{
    Console.WriteLine("1 delaying");
    await Task.Delay(2000);
    Console.WriteLine("2 delayed");
})
.ContinueWith(_ => Console.WriteLine("3 continue"));

Console.ReadKey();

Results in:

1 delaying
2 delayed
3 continue

In my case, I have to create a Task and start it later. How can I achieve that a created Task awaits Task.Delay() without executing the next Task (.ContinueWith()) first?

user11909
  • 1,235
  • 11
  • 27
  • 5
    There's no `Task` constructor which takes a `Func`. This means that your `async` lambda is being compiled to an `async void` method. So the `Task` has no way of knowning when the lambda is complete: it will look like it's done as soon as it hits the first await. `Task.Run` does have an overload which takes a `Func`. You might need to pass around a `Func` if you want to start it later. Remember that `new Task` was introduced before `async/await`, when C# used the fork/join model rather than the async/await model – canton7 Mar 28 '22 at 09:55
  • If you switch to `Task t = new Task(...);` you can write `t.ContinueWith(t2 => t2.Result.ContinueWith(_ => Console.WriteLine("3 continue")));` which will behave the way you want. If you don't want that, then you can't do this, as @canton7 says. – Lasse V. Karlsen Mar 28 '22 at 10:11
  • 3
    Try and avoid using `ContinueWith` at all: it is incredibly complex once you scratch the surface. Stick with plain `await` if you can: it's far more intuitive – canton7 Mar 28 '22 at 10:13
  • You might be able to wrap up some code with a `TaskCompletionSource`. – Lasse V. Karlsen Mar 28 '22 at 10:14
  • Try `Thread.Sleep()` instead. – Mohammad Mirmostafa Mar 28 '22 at 10:27
  • @MohammadMirmostafa why? Tasks aren't broken. That's a very bad idea, because it wastes a threadpool thread that could be used to execute other tasks. It wouldn't solve the actual problem either - using an `async void` lambda due to the misuse of the `Task` constructor – Panagiotis Kanavos Mar 28 '22 at 11:44
  • @user11909 There's no good reason to create a cold task and then call `Start()`. Change your `Main` to `async Task Main(...)` and either use `Task.Run(async ()=>{...})` to run your code, or just use it without an extra task. – Panagiotis Kanavos Mar 28 '22 at 11:47
  • @PanagiotisKanavos If you need to pause a thread (or task) for a while, as I know, there is no way else using `Thread.Sleep()`. or strange methods like `Semaphore` and `Mutex`. – Mohammad Mirmostafa Mar 28 '22 at 11:51
  • 1
    @MohammadMirmostafa a Task isn't a thread, it's a Promise. The whole point of using Tasks is to *avoid* freezing threads or creating new threads for background operations. Thread switching is an expensive operation. That's why `await Task.Delay()` is used. To release the thread so it can service other requests. When the timer behind `Task.Delay()` completes, execution will resume on another threadpool thread. When you freeze a threadpool thread you reduce the ability of the entire application to process background operations. In a web app, this would reduce the ability to service requests – Panagiotis Kanavos Mar 28 '22 at 12:00

0 Answers0