I've found that the following code won't actually wait for the task client.SendAsync() if I use the implementation:
taskList.Add(Task.Factory.StartNew(() => new Program().Foo()));
If I change it from Task.Factory.StartNew()
to just new Program().Foo()
or Task.Run(() => new Program.Foo()
it will correctly output some information. What are the differences between the two?
internal class Program
{
private async Task Foo()
{
while (true)
{
var client = new HttpClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Head, "http://www.google.com");
HttpResponseMessage response = await client.SendAsync(requestMessage);
Console.WriteLine(response.RequestMessage.RequestUri.ToString());
}
}
private static void Main(string[] args)
{
var taskList = new List<Task>();
// This won't output anything.
taskList.Add(Task.Factory.StartNew(() => new Program().Foo()));
// This will.
taskList.Add(Task.Run(() => new Program().Foo()));
// So does this.
taskList.Add(new Program().Foo());
Task.WaitAll(taskList.ToArray());
}
}
Based on this MSDN article, it seems Task.Run(someAction);
is equivalent to Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
But even when I change the code to this, it won't output anything. Why?
internal class Program
{
private async Task Foo()
{
while (true)
{
var client = new HttpClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Head, "http://www.google.com");
HttpResponseMessage response = await client.SendAsync(requestMessage);
Console.WriteLine(response.RequestMessage.RequestUri.ToString());
}
}
private static void Main(string[] args)
{
var taskList = new List<Task>();
taskList.Add(Task.Factory.StartNew(() => new Program().Foo(), CancellationToken.None,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
//taskList.Add(Task.Run(() => new Program().Foo()));
//taskList.Add(new Program().Foo());
Task.WaitAll(taskList.ToArray());
}
}
WaitAll() method in C# is used to wait for the completion of all the objects of the Task class. The Task class represents an asynchronous task in C#. We can start threads with the Task class and wait for the threads to finish with the Task. WaitAll() method in C#.
Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!
StartNew(Action<Object>, Object, CancellationToken, TaskCreationOptions, TaskScheduler) Creates and starts a task for the specified action delegate, state, cancellation token, creation options and task scheduler.
The Run method allows you to create and execute a task in a single method call and is a simpler alternative to the StartNew method. It creates a task with the following default values: Its cancellation token is CancellationToken.
The problem lays in the fact that Task.Factory.StartNew
isn't "task aware". Meaning, the return type from your method call to StartNew
is actually a Task<Task>
. This means that you're only waiting on the outter task to complete, not the inner one.
A simple solution to that would be to use the TaskExtensions.Unwrap()
method:
private static void Main(string[] args)
{
var taskList = new List<Task>();
taskList.Add(Task.Factory.StartNew(() => new Program().Foo()).Unwrap());
Task.WaitAll(taskList.ToArray());
}
Task.Run
does work because it is "task aware". It has an overload taking a Func<Task>
, which internally calls Unwrap
for you, returning only the inner task.
Task.Factory.StartNew
doesn't await the completion of the inner task by default. If you create a variable with the return value, you will get this:
Task<Task> task = Task.Factory.StartNew(() => new Program().Foo());
This means that the delegate returns as soon as the inner task hits the await statement. You need to call UnWrap
method to force the async execution:
Task task = Task.Factory.StartNew(() => new Program().Foo()).Unwrap();
taskList.Add(task);
Blog post you posted also explains this difference between synchronous and asynchronous delegates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With