How to correctly write async method? - c#

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();

Related

Create async Task<T> without run

How can I create a async Task but not run it right away?
private static async Task<string> GetString()
{
await Task.Delay(5000);
return "Finish";
}
Task<string> str = GetString();
This immediately starts the task.
If you want dereferred excecution use Func.
private static Func<Task<string>> GetStringFunc()
=> GetString;
Thus:
var deferred = GetStringFunc();
/*whatever*/
var task = deferred();
Update: please have a look at Lazy<T>.
You can create a cold Task by wrapping it in another Task.
The outer task creates the inner one without starting it.
//. If you don't call Start() and then await
//. Your code will never fire
public static async RunMe() {
var coldTask = new Task<Task<string>>(GetString);
// To start the task
coldTask.Start();
// And then let it finish
await coldTask;
}
// sharplab.io shows how C# compiles the async keyword
//. into a state machine that creates the inner task without starting it.

console app main method return string from async Task error [duplicate]

This question already has answers here:
Can't specify the 'async' modifier on the 'Main' method of a console app
(20 answers)
Closed 5 years ago.
I have this simple code:
public static async Task<int> SumTwoOperationsAsync()
{
var firstTask = GetOperationOneAsync();
var secondTask = GetOperationTwoAsync();
return await firstTask + await secondTask;
}
private async Task<int> GetOperationOneAsync()
{
await Task.Delay(500); // Just to simulate an operation taking time
return 10;
}
private async Task<int> GetOperationTwoAsync()
{
await Task.Delay(100); // Just to simulate an operation taking time
return 5;
}
Great. This compiles.
But let’s say I have a console application and I want to run the code above (calling SumTwoOperationsAsync()).
static void Main(string[] args)
{
SumTwoOperationsAsync();
}
But I've read that (when using sync) I have to sync all the way up and down:
Does this mean that my Main function should be marked as async?
Well, it can't be because there is a compilation error:
an entry point cannot be marked with the 'async' modifier
If I understand the async stuff , the thread will enter the Main function → SumTwoOperationsAsync → will call both functions and will be out. But until the SumTwoOperationsAsync
What am I missing?
In most project types, your async "up" and "down" will end at an async void event handler or returning a Task to your framework.
However, Console apps do not support this.
You can either just do a Wait on the returned task:
static void Main()
{
MainAsync().Wait();
// or, if you want to avoid exceptions being wrapped into AggregateException:
// MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
...
}
or you can use your own context like the one I wrote:
static void Main()
{
AsyncContext.Run(() => MainAsync());
}
static async Task MainAsync()
{
...
}
More information for async Console apps is on my blog.
Here is the simplest way to do this
static void Main(string[] args)
{
Task t = MainAsync(args);
t.Wait();
}
static async Task MainAsync(string[] args)
{
await ...
}
As a quick and very scoped solution:
Task.Result
Both Task.Result and Task.Wait won't allow to improving scalability when used with I/O, as they will cause the calling thread to stay blocked waiting for the I/O to end.
When you call .Result on an incomplete Task, the thread executing the method has to sit and wait for the task to complete, which blocks the thread from doing any other useful work in the meantime. This negates the benefit of the asynchronous nature of the task.
notasync
My solution. The JSONServer is a class I wrote for running an HttpListener server in a console window.
class Program
{
public static JSONServer srv = null;
static void Main(string[] args)
{
Console.WriteLine("NLPS Core Server");
srv = new JSONServer(100);
srv.Start();
InputLoopProcessor();
while(srv.IsRunning)
{
Thread.Sleep(250);
}
}
private static async Task InputLoopProcessor()
{
string line = "";
Console.WriteLine("Core NLPS Server: Started on port 8080. " + DateTime.Now);
while(line != "quit")
{
Console.Write(": ");
line = Console.ReadLine().ToLower();
Console.WriteLine(line);
if(line == "?" || line == "help")
{
Console.WriteLine("Core NLPS Server Help");
Console.WriteLine(" ? or help: Show this help.");
Console.WriteLine(" quit: Stop the server.");
}
}
srv.Stop();
Console.WriteLine("Core Processor done at " + DateTime.Now);
}
}

await WebClient.DownloadStringTaskAsync waits forever but accessing Result property works

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!");
}
}

Async Task await

I am trying to understand async method in c#
I have created a wcf service that delay 10 seconds. I am calling the wcf service from a console application the value asyncValue is never sets. It just closes the console app.
Why is the asyncValue never set to Ok?
Wcf Service
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AsyncService
{
[OperationContract]
public async Task<string> TimeAsync()
{
await Task.Delay(10000);
return "Ok";
}
}
Console application
static void Main(string[] args)
{
Task<string> asyncValue = GetAsyncTimeFromWcf();
}
private static async Task<string> GetAsyncTimeFromWcf()
{
AsyncService.AsyncServiceClient client = new AsyncService.AsyncServiceClient();
Task<string> asyncTime = client.TimeAsync();
return await asyncTime;
}
For the await keyword to make sense you need to do something with the return value. The purpose of the async/await feature is to allow the application to continue with other, non-related, code until you need to use the result of the awaited task.
In your case, you call await but you don't use the return value, so the Main method continues and effectively exists the application. For example, try
static void Main(string[] args)
{
Task<string> asyncValue = GetAsyncTimeFromWcf();
Console.WriteLine("Waiting for server...");
Console.WriteLine(String.Format("Result = {0}", asyncValue.Result);
Console.ReadKey();
}
You should find the console will output Waiting for server... for 10 seconds then once the server returns it will display Ok.
asyncValue is a Task whose Result will contain the final value when it finishes. You have to wait for this result either by calling asyncValue.Result or by calling asyncValue.Wait() and then check asyncValue.Result.
You can't use await asyncValue or await GetAsyncTimeFromWcf() in the Main method of a Console program, because you can't mark it as async.
Just try:
Task<string> asyncValue = GetAsyncTimeFromWcf();
var time=asyncValue.Result;
Console.WriteLine(time);
Your main method ends before the WCF call returns.
To see the result, you can add the line:
Console.WriteLine(asyncValue.Result);
The call to Result, will Wait for the task to complete, and then get the result.

HttpClient.PostAsync knocks out the app with exit code 0

Everything was working today until it stopped... Below is the minimum source code (I'm using VS 2012 Update 1, .Net 4.5). When I run it, app exits upon calling client.PostAsync() and so it never reaches Console.ReadLine(). Same in debugger, no exception, nothing, exit code 0.
I tried rebooting machine, restarting VS2012 - nothing works.
Again, everything was running today, not sure what changed (no software has been installed etc, all other network apps still work).
Any ideas? I think I'm loosing my mind.
class Program
{
static void Main(string[] args)
{
Run();
}
private async static void Run()
{
using (var client = new System.Net.Http.HttpClient())
{
var headers = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("submit.x", "48"),
new KeyValuePair<string, string>("submit.y", "15"),
new KeyValuePair<string, string>("submit", "login")
};
var content = new FormUrlEncodedContent(headers);
HttpResponseMessage response = await client.PostAsync("http://www.google.com/", content);
Console.ReadLine();
}
}
}
Your problem is that a program normally exits when its Main() method finishes. And your Main() finishes as soon as you hit the await in Run(), because that's how async methods work.
What you should do is to make Run() into an async Task method and then wait for the Task in your Main() method:
static void Main()
{
RunAsync().Wait();
}
private static async Task RunAsync()
{
…
}
In C# 7.1+ you should use async Main instead:
static async Task Main()
{
await RunAsync();
}
private static async Task RunAsync()
{
…
}
Few more notes:
You should never use async void methods, unless you have to (which is the case of async event handlers).
Mixing await and Wait() in a GUI application or in ASP.NET is dangerous, because it leads to deadlocks. But it's the right solution if you want to use async in a console application.

Categories

Resources