await WebClient.DownloadStringTaskAsync waits forever but accessing Result property works - c#

The following piece of code works fine (prints the markup on the console):
System.Net.WebClient wc = new System.Net.WebClient();
var task1 = wc.DownloadStringTaskAsync("https://www.google.com.au");
Console.WriteLine(task1.Result); // works
But when I await the task it just awaits forever and the task's state is WaitingForActivation:
System.Net.WebClient wc = new System.Net.WebClient();
var task1 = wc.DownloadStringTaskAsync("https://www.google.com.au");
Console.WriteLine(await task1);
Console.WriteLine("done!"); // this never gets printed
What am I missing?
EDIT: full code:
static void Main(string[] args)
{
DoIt();
}
static async void DoIt()
{
System.Net.WebClient wc = new System.Net.WebClient();
var task1 = wc.DownloadStringTaskAsync("https://www.google.com.au");
Console.WriteLine(await task1);
Console.WriteLine("done!"); // this never gets printed
}

What am I missing?
Currently, the way you're invoking your code, I see no way as to how the task is WaitingForActivation. As soon as DoIt hits the first await, it yields control back to the caller, which is your Main method, which should terminate and close your console.
Using Task.Result works because it synchronously blocks on the async method, hence it waits for it to complete and you see the result printed to the console.
What you actually need to do is make DoIt async Task instead of async void, and use Task.Wait there to synchronously block so Main won't terminate:
static void Main(string[] args)
{
DoItAsync().Wait();
}
static async Task DoItAsync()
{
System.Net.WebClient wc = new System.Net.WebClient();
var task1 = wc.DownloadStringTaskAsync("https://www.google.com.au");
Console.WriteLine(await task1);
Console.WriteLine("done!"); // this never gets printed
}
Note using Task.Result and Task.Wait is only to make the console not terminate. In any other environment (UI based or ASP.NET), you shouldn't ever block on async code and should always await on async methods.

You are not await-ing or Wait()-ing your DoIt() method which is async. As Main() can't be made async, your only option is to synchronously wait for the task to complete.
Also, it is not possible to await or Wait() async method which returns void so we have to make it to return Task (or Task<T>, if method has to return some value). Callers of the async method which returns void cannot know when it returns and also whether it throws any exceptions.
using System;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Program.DoIt().Wait();
}
private static async Task DoIt()
{
System.Net.WebClient wc = new System.Net.WebClient();
var task1 = wc.DownloadStringTaskAsync("https://www.google.com.au");
Console.WriteLine(await task1);
Console.WriteLine("done!");
}
}

Related

Why await sometimes create new thread but sometimes not?

class Program
{
static void Main(string[] args)
{
var rst = DownloadPage("http://www.baidu.com");
//var rst2=GetString();
Console.ReadKey();
}
private static async Task<string> DownloadPage(string url)
{
using (var client = new HttpClient())
{
PringMsgWithThreadId("Before await");
var response = await client.GetAsync(url).ConfigureAwait(continueOnCapturedContext:false);
var content= await response.Content.ReadAsStringAsync();
PringMsgWithThreadId(content.Substring(0, 10));
PringMsgWithThreadId("After await");
return content;
}
}
private static async Task<string> GetString()
{
PringMsgWithThreadId("Before await");
var result = await GetStringAsync();
PringMsgWithThreadId(result);
PringMsgWithThreadId("After await");
return result;
}
private static Task<string> GetStringAsync()
{
var task = new Task<string>(() =>
{
Thread.Sleep(1000 * 2);
return "string after sleep two seconds";
});
task.RunSynchronously();
return task;
}
private static void PringMsgWithThreadId(string tag)
{
Console.WriteLine($"{tag}(ThreadId:{Thread.CurrentThread.ManagedThreadId})");
}
}
output when run DownloadPage() method output:
output when run GetString() method
My question:
1.when call DownloadPage(),why code after await executed in the thread(ThreadID:15) other than main thread(ThreadId:10).
2.when call GetString(),why code after await executed in the same thread(both threadId is 10)。
await never creates a new thread.
As explained on my async intro, await will first examine its argument (the task). If it has already completed, then it continues executing synchronously. Otherwise, it "pauses" the method and registers a callback with its argument (i.e., places a continuation on the task).
Later, when the task completes, the continuation will run. Since you're in a Console app without a SynchronizationContext/TaskScheduler, that continuation will run on a thread pool thread.
So, the answer to your first question is that the main thread is busy (blocked in Console.ReadKey), and also the main thread in a Console app isn't a thread pool thread anyway. The answer to your second question is because the task in GetStringAsync is run synchronously and is already complete by the time it returns, and this causes the await in GetString to continue (synchronously).
On a side note, you should never, ever, use the task constructor. If you want to return an already-completed task, use Task.FromResult. If you want to execute some work on a background thread, use Task.Run.

How do I get async code to wait until all tasks are complete?

When I run the code below, the message "Press Enter to continue... " appears before the results are returned from the HttpClient.GetAsync() calls are completed. The actual sequence of events: the GetAsync() calls are made, the "Press Enter..." message appears, and then the results are one-by-one output to the console window. How do I wait until all the GetAsync() calls are complete before displaying the "Press Enter..." message?
class Program
{
static HttpClient client = new HttpClient();
static void Main(string[] args)
{
RunAsync().Wait();
Console.WriteLine("\n\n\n\nPress Enter to continue... ");
Console.ReadLine();
}
static async Task RunAsync()
{
List<string> urls = new List<string>()
{
"http://www.domain1.com",
"http://www.domain2.com",
"http://www.domain3.com",
"http://www.domain4.com"
};
foreach (var url in urls)
{
DownloadPageAsync(url);
}
}
static async Task<string> DownloadPageAsync(string url)
{
Console.WriteLine("Starting: " + url);
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
// do stuff here
}
Console.WriteLine("Done: " + url);
return response.Content.ToString();
}
}
Since DownloadPageAsync returns a task, you can make a list of all tasks and wait on them all:
Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))
Or simplified:
Task.WhenAll(urls.Select(DownloadPageAsync))
The #patrik-hofman answer is a good one (up voted) although, see my comment
If you would rather the requests to happen sequentially...
Add await to the DownloadPageAsync line.
You've used async in RunAsync but there are no awaits. So, although it returns a Task it is not waiting for the DownloadPageAsync call to complete. This means that the method simply returns an "empty" Task which completes immediately. So your .Wait() is waiting for nothing.
I think the problem is that you are not awaiting the DownloadPageAsync method inside the RunAsync() method. If you update the RunAsync() method to the code below then I believe it will work as you expect:
static async Task RunAsync()
{
List<string> urls = new List<string>()
{
"http://www.domain1.com",
"http://www.domain2.com",
"http://www.domain3.com",
"http://www.domain4.com"
};
foreach (var url in urls)
{
// added await here
await DownloadPageAsync(url);
}
}
You need to create different tasks per every call you want, in your example you are running the code and not waiting for the call. When you call WhenAll that simply creates a task for all of them. Let's say that you use the code bellow and inside each MakeYouCall method you insert an item on a list. That list will be a shared resource that you need to lock. When the WhenAll is made, then if you don't wait for the result(make a call to Wait() ) then the collection could be partially filled.
var register1 = new Action(() => MakeYourCall1());
var register2 = new Action(() => MakeYourCall2());
var register3 = new Action(() => MakeYourCall3());
then
var t1 = Task.Factory.StartNew(register1);
var t2 = Task.Factory.StartNew(register2);
var t3 = Task.Factory.StartNew(register3);
after that you can call the WhenAll that will return a Task, then wait for it.
Task.WhenAll(t1, t2, t3).Wait();

MSDN example on async/await - can't I reach the break point after the await call?

In trying MSDN's example on async/await, why I can't reach a break point after the await operator ?
private static void Main(string[] args)
{
AccessTheWebAsync();
}
private async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
/*** not relevant here ***/
//DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
My understanding is that the await is a construct that abstracts the asynchronous flow from the developer - leaving him/her as if working synchronously. In other words, in the code above, I do not care about how and when the getStringTask finishes, I care only about it finishing and using its results. I would expect then to be able to reach the break point after the await call at sometime.
You call your asynchronous method from a Console application's Main method without waiting for the async method to finish. As a result, your process terminates before your task has a chance to complete.
Since you can't convert a Console application's Main to an asynchronous (async Task) method, you'll have to block on the asynchronous method, by calling Wait or .Result:
private static void Main(string[] args)
{
AccessTheWebAsync().Wait();
}
or
private static void Main(string[] args)
{
var webTask=AccessTheWebAsync();
//... do other work until the resuls is actually needed
var pageSize=webTask.Result;
//... now use the returned page size
}

When should Task.Run() be used?

What's the difference between these two approaches:
public static int Main(string[] args)
{
string result;
Task.Run(async () =>
{
Task<string> getStringTask = GetStringAsync();
result = await validationsTask;
}).Wait();
Console.WriteLine(result);
}
and
public static int Main(string[] args)
{
Task<string> getStringTask = GetStringAsync();
getStringTask.Wait();
string result = getStringTask.Result;
Console.WriteLine(result);
}
I've seen a lot of people using the first approach and I'm not sure why. Is there any particular advantage? Which one is recommended for waiting async methods inside main of a Console Application?
Is there any particular advantage?
Usually with async methods the operation is initialized synchronously and then the wait can be asynchronous with await or syncrhnous with Wait(). The Main method can't be async so you are force to block with Wait() there or you can do a Console.ReadKey() to run until the user presses a key.
Task.Run(async () => ... ) can be quite useful when the async operation is expensive to initialize. That way you allow the main thread to continue while the operation is initializing.
Which one is recommended for waiting async methods inside main of a Console Application?
I would use a slightly modified version of the second approach. You can add a MainAsync method and call that from Main then you can use await inside it.
public static async Task MainAsync()
{
string result = await GetStringAsync();
Console.WriteLine(result);
}
public static int Main(string[] args)
{
MainAsync().Wait();
}
Also with console apps there is no risk of deadlock as there is no SynchronizationContext and the default thread pool one gets used.
The first approach continues execution after the asynch function is finished using a thread pool thread while the second approach continues execution using the calling thread that starts the asynch function.
With the second approach, there is a possibility of deadlocks. For example (similar to an example extracted from the book CLR via C#):
public static int Main(string[] args)
{
Task<string> getStringTask = GetStringAsync();
string result = getStringTask.Result; //the main thread is blocked waiting.
Console.WriteLine(result);
}
public Task<string> GetStringAsync()
{
// Issue the HTTP request and let the thread return from GetHttp
HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/");
// We never get here: The main thread is waiting for this method to finish but this method
// can't finish because the main thread is waiting for it to finish --> DEADLOCK!
return await msg.Content.ReadAsStringAsync();
}
So the first approach avoids this problem:
public static int Main(string[] args)
{
string result;
Task.Run(async () =>
{
// We run on a thread pool thread
Task<string> getStringTask = GetStringAsync();
// We do get here because any thread pool thread can execute this code, we don't need the main thread.
result = await validationsTask;
}).Wait();
Console.WriteLine(result);
}
Another solution is using ConfigureAwait(false), extracted from the book:
Passing true to this method gives you the same behavior as not calling
the method at all. But, if you pass false, the await operator does
not query the calling thread’s SynchronizationContext object and, when
a thread pool thread completes theTask, it simply completes it and the
code after the await operator executes via the thread pool thread.
public Task<string> GetStringAsync()
{
HttpResponseMessage msg = await new HttpClient().GetAsync("http://Wintellect.com/").ConfigureAwait(false);
// We DO get here now because a thread pool can execute this code
// as opposed to forcing the main thread to execute it.
return await msg.Content.ReadAsStringAsync().ConfigureAwait(false);
}

How to correctly write async method?

So I am trying to learn the basics of using 'async' and 'await' in C#, but I am not sure what I am doing wrong here. I am expecting the following output:
Calling DoDownload
DoDownload done
[...output here...]
But I don't get the output of the download, and I also expect "done" but that takes a while. Shouldn't that be output immediately? Also, I can't seem to get the string result either. Here is my code:
namespace AsyncTest
{
class Program
{
static void Main(string[] args)
{
Debug.WriteLine("Calling DoDownload");
DoDownloadAsync();
Debug.WriteLine("DoDownload done");
}
private static async void DoDownloadAsync()
{
WebClient w = new WebClient();
string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
Debug.WriteLine(txt);
}
}
}
To get the behavior you want you need to wait for the process to finish before you exit Main(). To be able to tell when your process is done you need to return a Task instead of a void from your function, you should never return void from a async function unless you are working with events.
A re-written version of your program that works correctly would be
class Program
{
static void Main(string[] args)
{
Debug.WriteLine("Calling DoDownload");
var downloadTask = DoDownloadAsync();
Debug.WriteLine("DoDownload done");
downloadTask.Wait(); //Waits for the background task to complete before finishing.
}
private static async Task DoDownloadAsync()
{
WebClient w = new WebClient();
string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
Debug.WriteLine(txt);
}
}
Because you can not await in Main() I had to do the Wait() function instead. If this was a application that had a SynchronizationContext I would do await downloadTask; instead and make the function this was being called from async.
You are calling DoDownloadAsync() but you don't wait it. So your program going to the next line. But there is another problem, Async methods should return Task or Task<T>, if you return nothing and you want your method will be run asyncronously you should define your method like this:
private static async Task DoDownloadAsync()
{
WebClient w = new WebClient();
string txt = await w.DownloadStringTaskAsync("http://www.google.com/");
Debug.WriteLine(txt);
}
And in Main method you can't await for DoDownloadAsync, because you can't use await keyword in non-async function, and you can't make Main async. So consider this:
var result = DoDownloadAsync();
Debug.WriteLine("DoDownload done");
result.Wait();

Categories

Resources