0

From what I've read, I thought Task.Run was the same as Task.Factory.StartNew. With Task.Run just a newer and preferred way of doing things. However, I'm seeing a slight difference in behaviour with the following code.

I've created a simple .Net 6 console app.

Worker.cs has the content:

internal class Worker
{
    private readonly HttpClient httpClient = new();

    public async Task StartTaskRun(CancellationToken token)
    {
        await Task.Run(async () => await SomeWork(token));
    }

    public async Task StartTaskFactory(CancellationToken token)
    {
        await Task.Factory.StartNew(async () => await SomeWork(token));
    }

    private async Task SomeWork(CancellationToken token)
    {
        try
        {
            while (true)
            {
                var result = await httpClient.GetStringAsync("https://www.yahoo.com", token);
                await Task.Delay(5000, token);
            }
        }
        catch (TaskCanceledException)
        {
            Console.WriteLine("Task has been canceled");
        }
    }
}

And Program.cs has the content:

var cancellationTokenSource = new CancellationTokenSource();

var worker = new Worker();

//worker.StartTaskFactory(cancellationTokenSource.Token); // 1# Returns
//worker.StartTaskRun(cancellationTokenSource.Token); // 2# Returns

//await worker.StartTaskFactory(cancellationTokenSource.Token); // 3# Returns
//await worker.StartTaskRun(cancellationTokenSource.Token); // #4 Does not return

Console.WriteLine("Hello world");
Console.ReadLine();

cancellationTokenSource.Cancel();

Console.WriteLine("Cancelling");
Console.ReadLine();

In Program.cs, I have 4 different ways that I call the Start methods. The #1 and #2 calls work as intended. They make a fire and forget call to the async methods and the console shows the "Hello World" output and continues with the logic.

However, why does #3 and #4 differ in results? #3 continues execution in the same way as #1 and #2. However, #4 seems to continue to wait on the StartTaskRun() call?

Can anyone explain the difference in results on this please?

millie
  • 2,642
  • 10
  • 39
  • 58
  • 1
    Does this answer your question? [What is the difference between Task.Run() and Task.Factory.StartNew()](https://stackoverflow.com/questions/38423472/what-is-the-difference-between-task-run-and-task-factory-startnew) – MonkeyDLuffy Feb 07 '23 at 13:14
  • `Task.Run` has overloads designed to work correctly with `async` lambdas. `TaskFactory.StartNew` does not. You can't just assume they'll work the same without considering if you're calling "similar" overloads of both. – Damien_The_Unbeliever Feb 07 '23 at 13:21
  • @MonkeyDLuffy As good as those answers are, I canot actually spot the answer to this question anywhere in there – canton7 Feb 07 '23 at 13:23
  • The difference is that `Task.Run` has an overload which takes a `Func`, whereas `TaskFactory.StartNew` only has overloads which take an `Action`. Therefore if you pass an `async () => await ...` to `TaskTFactory`, it gets turned into an `async void` method, which means that `TaskFactory` has no way of knowing when the operation started by the delegate actually completes – canton7 Feb 07 '23 at 13:24
  • In `SomeWork()` you are not checking the cancellationToken so unless `GetStringAsync` and `Task.Delay` call `ThrowIfCancellationRequested` or throw an exception themselves, you will be in an endless loop. – Mike Mozhaev Feb 07 '23 at 13:28

1 Answers1

1

The simple rules are:

  • Task.Factory.StartNew existed before async-await.
  • Task.Run was introduced with async-await.
  • You should REALLY know when you need to use Task.Factory.StartNew.
    • And it's almost never.
      • For most developers it will be "never in a lifetime".
  • If you are not sure, ALWAYS use Task.Run.
Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59