Running Callback Function in Async - c#

I'm trying to understand how to run callback functions in an async console application. My base console app code looks as follows:
using Nito.AsyncEx;
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync());
}
static async Task MainAsync()
{
}
The method that I want to run in Async mode is the following from a websocket api:
using ExchangeSharp;
public static void Main(string[] args)
{
// create a web socket connection to Binance. Note you can Dispose the socket anytime to shut it down.
// the web socket will handle disconnects and attempt to re-connect automatically.
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}
The above code is meant to lock a console app and subscribe to an event which receives data, and to do something with the received data.
What I want to do is run the above in a separate thread or in async so that I can continue with my code in the MainAsync() function.
My C# experience with this is limited. Will appreciate any help!

According to source code GetTickersWebSocket isn't a blocking call.
The only blocking call you've posted is Console.ReadLine.
ExchangeBinanceAPI has its own callback-based asynchrony, so, just throw away Console.ReadLine, or place more code before it:
static async Task MainAsync()
{
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
// code continues here
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}
As a side note.
I'm not familiar with this project, but source code shows poor man's aynchrony inside WebSocketWrapper:
Task.Factory.StartNew(ListenWorkerThread)
// inside ListenWorkerThread
_ws.ConnectAsync(_uri, CancellationToken.None).GetAwaiter().GetResult();
result = _ws.ReceiveAsync(receiveBuffer, _cancellationToken).GetAwaiter().GetResult();
and so on.
There are attempts to call asynchronous code in synchronous way.
Instead of this, at least ListenWorkerThread must be converted to async method, and it definitely must not be called via Task.Factory.StartNew.
I'd post a request to rewrite code in true asynchronous manner, if I have to use this project.

Async method would not block the caller thread if you use it directly, you can just call it anywhere before Consolo.ReadLine() then use returned Task to handle the result if you need.
public static void Main(string[] args)
{
// Would not block the thread.
Task t = MainAsync();
// Only if you need. Would not block the thread too.
t.ContinueWith(()=> { code block that will run after MainAsync() });
// create a web socket connection to Binance. Note you can Dispose the socket anytime to shut it down.
// the web socket will handle disconnects and attempt to re-connect automatically.
ExchangeBinanceAPI b = new ExchangeBinanceAPI();
using (var socket = b.GetTickersWebSocket((tickers) =>
{
Console.WriteLine("{0} tickers, first: {1}", tickers.Count, tickers.First());
}))
{
Console.WriteLine("Press ENTER to shutdown.");
Console.ReadLine();
}
}

Related

Why do I need to use Wait when Sending email using SendGrid C# client library in a console app

I am using the C# client library for SendGrid version 9.27.0.
The only way I can get it to send an email is by adding Wait() to end of my method.
I understand the await operator is a promise to return back to the point in code after the asynchronous method is finished.
But why do I need to add Wait()? Isn't that converting the asynchronous method to synchronous? If so, what's the point in making it async?
Program.cs
static void Main(string[] args) {
//var customerImport = new CustomerImport();
//customerImport.DoImport();
var mailClient = new MailClient();
var recipients = new List<string>();
recipients.Add("test#lobbycentral.com");
//Never sends an email
var response = mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false);
//Will send an email
mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false).Wait();
}
MailClient.cs
public async Task SendMail(string emailFrom, List<string> emailTo, string subject, string body, bool isPlainText) {
try {
var apiKey = Utils.GetConfigValue("sendgridAPIKey");
var emails = new List<EmailAddress>();
foreach (string email in emailTo) {
emails.Add(new EmailAddress(email));
}
var plainTextContent = "";
var htmlContent = "";
if (!isPlainText) {
htmlContent = body;
} else {
plainTextContent = body;
}
var message = MailHelper.CreateSingleEmailToMultipleRecipients(new EmailAddress(emailFrom, "LobbyCentral"), emails, subject, plainTextContent, htmlContent);
//if (metaData != null)
// message.AddCustomArgs(metaData);
foreach (string filename in FileAttachments) {
if (System.IO.File.Exists(filename)) {
using (var filestream = System.IO.File.OpenRead(filename)) {
await message.AddAttachmentAsync(filename, filestream);
}
}
}
foreach (PlainTextAttachmentM plainTextM in PlainTextAttachments) {
byte[] byteData = Encoding.ASCII.GetBytes(plainTextM.Content);
var attachment = new Attachment();
attachment.Content = Convert.ToBase64String(byteData);
attachment.Filename = plainTextM.AttachmentFilename;
attachment.Type = "txt/plain";
attachment.Disposition = "attachment";
message.AddAttachment(attachment);
}
var client = new SendGridClient(apiKey);
var response = await client.SendEmailAsync(message);
if (response.IsSuccessStatusCode) {
if (DeleteAttachmentsAfterSend && FileAttachments.Count > 0) {
foreach (string filename in FileAttachments) {
if (System.IO.File.Exists(filename)) {
System.IO.File.Delete(filename);
}
}
}
} else {
Utils.DebugPrint("error sending email");
}
} catch (Exception ex) {
throw new Exception(string.Format("{0}.{1}: {2} {3}", System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, ex.StackTrace));
}
}
Calling mailClient.SendMail starts a Task, but doesn't wait for its completion.
If that's the last instruction of you program, the program simply ends before the tasks can finish.
You want your last instructions to start and wait for the task's completion. You can do that by using either of the following.
Make your Main async. (That's what I would personally do.)
// Signature changed to async Task instead of void.
static async Task Main(string[] args) {
// (...)
// Added await. Removed Wait.
await mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false);
}
Use Wait like you're doing.
static void Main(string[] args) {
// (...)
var task = mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false);
task.Wait();
}
Question : But why do I need to add Wait()? Isn't that converting the asynchronous method to synchronous? If so, what's the point in making it async?
Answer : It's not mandatory to Wait() a Task. What's the point in making it async? It gives you two great benefits.
You can return Task, which means it's awaitable, and which means again, you can choose either wait it or just forget about it and let the task scheduled and picked up by a threadPool thread instead of synchronously running it in the same thread.
You can use async/await pattern, which means you can gracefully avoid blocking the current thread
Below code obviously will block the current thread.
static void Main(string[] args)
{
.
.
//Will send an email
mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false)
.Wait(); // <- block the current thread until the task completes.
}
If SendMail is something you can fire&forget in your real codebase, you can just get rid of .Wait() and move on without checking the Task state. But your application should be up and running for a certain, enough time to finish the scheduled task. If it's not, you'd better think of using async/await pattern instead of blocking your valuable thread by using .Wait().
static async Task Main(string[] args)
{
.
.
//Will send an email and get to know when the task is done.
await mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false);
}
You need to await it in this console app because the very last thing that the Main() does before it returns (and the app exits) is send the mail. I'd say there is slim to no chance at all that the mail sending task will complete before the app exits; it involves a lot of IO, whereas it will take nanoseconds for the app to quit, taking the incomplete Task with it
Anything you do to make it wait long enough will suffice; turning it synchronous with a call to Wait, making the Main as async Task Main(...), even adding a Console.ReadLine will all mean the mail sending will have time to complete ..
.. though I question why a console app that only does one thing then exits even needs to be async anything - it simply has no other jobs to do while it waits for this IO to complete so there seems little point using any asyncronous facilities
TL;DR
Update your code to use async, await, and Task's throughout the whole call chain of your method, including your Main method, otherwise your program won't wait for the asynchronous Task's to complete and will not run all your code.
Your code should look like this:
static async Task Main(string[] args) {
//var customerImport = new CustomerImport();
//customerImport.DoImport();
var mailClient = new MailClient();
var recipients = new List<string>();
recipients.Add("test#lobbycentral.com");
//Never sends an email
var response = await mailClient.SendMail("noreply#lobbycentral.com", recipients, "Test email", "This is a test of the new SG client", false);
}
In C# .NET the Task class represents an operation that usually is performed asynchronously on another thread. When you use the await keyword or the .Wait() method, the program will wait for the operation to finish and then continue executing the next statements.
The difference between using the await keyword and the .Wait() method is that the await keyword will release the current thread so other work can be done on the current thread, while the .Wait() method will block the current thread, leaving the thread unutilized until the task is complete.
When the task is complete, when using the async keyword, the program will find an available thread (not necessarily the same thread as you started on) and will continue to run the instructions subsequent to your await. The .Wait() blocks the current thread, so it'll keep using the current thread when the task is complete.
To use await you need to make your method async. Sometimes, you don't need to wait on the Task and you can simply return it, in that case you don't need to mark the method as async although it is still recommended for easier debugging.
However, once you start using async and Task's, you need to do so all the way up to the call chain.
This sample using .Wait needs to be converted to using async/await.
public static class Program
{
public static void Main(string[] args)
{
DoSomeWork();
}
public static void DoSomeWork()
{
DoFileOperations();
}
public static void DoFileOperations()
{
File.ReadAllTextAsync("/path/to/file").Wait();
}
}
Making this program asynchronous looks like this:
public static class Program
{
public static async Task Main(string[] args)
{
await DoSomeWork();
}
public static Task DoSomeWork()
{
// you can return a Task without awaiting it, as a result no need for async keyword.
return DoFileOperations();
// But the following is recommended for easier debugging, at cost of a negligible performance hit. (Also need to mark method is async for this)
// return await DoFileOperations();
}
public static async Task DoFileOperations()
{
await File.ReadAllTextAsync("/path/to/file");
}
}
For a console program, you need to update the Main method signature to use async Task or Task for this to work. If you don't, the Task will start running but the program will exit immediately and not wait for the Task to be complete.
(Non-console applications will provide other APIs to do async/await.)
In your application, your Main method does not have the async Task or Task return signature, and you're not awaiting the Task returned from the mailClient.SendMail method.
What likely is happening inside of your mailClient.SendMail method is
The first asynchronous Task is started, created by the message.AddAttachmentAsync
This Task runs on a separate thread, meanwhile the current thread goes back to run the code after mailClient.SendMail in the Main method
The Main method finishes and the console application stops running.
At this point, the Task from message.AddAttachmentAsync is likely finished already, but the subsequent code is never run, including the client.SendEmailAsync method, because the Task from mailClient.SendMail wasn't awaited.
There's a lot more nuance to async, await, and Task's, but those details aren't too relevant to your questions, so if you're interested, here's more info on Asynchronous programming with async and await from the Microsoft docs.
I'd also recommend reading David Fowler's guidance on async/await which lists some pitfalls and best practices.

Use of Async and await in console Application

I have four methods.
Main: only calls the preform method
Working: displays "please wait for the user"
Taking Time: A program that takes time to execute.
Preform: Calls the taking time and working methods asynchronously.
The following is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncObservation
{
class Program
{
static void Main(string[] args)
{
preform();
}
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
Thread.Sleep(1000);
Console.WriteLine("The Program finished");
}
public static async void preform()
{
Task timer = Takingtime();
Task wait = Working();
}
}
}
In the end: I need to display
This program started.
Please wait, the program is running
The program ended.
I see several problems in your program.
Although Preform is neither async nor an event handler, it does not return a Task
The tasks started in Preform are not awaited for before you finish Preform. Hence you never know when they are finished, nor what the results are (exception?). You could even end your program before they are finished
After you start a Task it is not guaranteed when it will run. You can only be sure that statements are already executed if you await for the Task.
Using async-await is a method to make sure that your thread looks around to see if it can do useful stuff instead of waiting idly if it has to wait for something. Thread.Sleep is a busy wait. If you want to look around to see if you can do something else use await Task.Delay(TimeSpan.FromSeconds(1)) instead.
In your case, you can't be sure that any Console line has been written until you await the procedure that should write your line. If you start your second Task before awaiting the first, you don't know how far the first task already proceeded, and thus you don't know for sure that the text already has been written to the Console.
C# 7.1 introduced async Task Main(), so you could use that instead of the traditional void Main. It saves you from catching and interpreting the AggregateException that is thrown by the Task you start to make your process async.
If you don't want to use the async Main, you can of course use Task.Run to call an async function:
static void Main(string[] args)
{
try
{
var preformTask = Task.Run( () => Preform() );
DoSomethingElse(); // if needed
preformTask.Wait(); // wait for preformTask to finish
Console.WriteLine("Task completed; press any key to finish");
Console.ReadKey();
}
catch (Exception exc) // inclusive ggregateException if one of your Task fails
{
ProcessException(exc)
}
}
static async Task preform()
{
// To be certain that the Console Line has been written: await
await Takingtime();
// if here, you are certain that the Line has been written,
// or course you have lost parallel processing
await Working();
}
For completeness: the other functions
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
// either return a completed Task, or await for it (there is a difference!
await Task.CompletedTask;
// or:
return Task.CompletedTask; // do not declare async in this case
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
//Use Task.Delay instead of Sleep
await Task.Delay(TimeSpan.FromSeconds(1); // improved readability
Console.WriteLine("The Program finished");
}
Because of the awaits in Preform you are certain that the text has been written. However, you've lost some parallellism.
If you really want those procedures to execute at the same time, you can't be certain about when text will be written. If that is important, then split the Parts that should be run first (write Console) from the parts that should run in parallel (Task.Delay)
static async Task preform()
{
// Do the things that should be done before parallel tasks are run
await DoThisFirst();
// start the Tasks that can work parallel: not sure what statements are executed first
var taskA = DoTaskA();
var taskB = DoTaskB();
// if here, you are free to do something else
// can't be sure about the status of taskA nor taskB
DoSomethingElse();
// if you want to do something after you know that the tasks have completed:
// await the tasks here:
await Task.When (new Task[] {taskA, taskB});
// if here, you are certain that all parallel tasks have completed successfully
// if desired fetch the return values of the Tasks:
var returnValueA = taskA.Result;
var returnValueB = taskB.Result;
// do all async things of which you needed to be certain that both tasks finished
// for example:
await ProcessResults(returnValueA, returnValueB);
}
In a console app it is OK to use a .Wait() call in an void Main method.
In some contexts where there is synchronisation necessary .Wait() can cause deadlocks (ASP.NET has a request to synchronise on or XAML/WinForms which have a UI thread), but there is nothing to synchronise on here.
static void Main()
{
preform.Wait()
}
This will wait for the async work to complete synchronously. You need the method to run synchronously so that it does not return early. If you use async void and await the method will return immediately and exit the method, await does not work in a Main method.
I would also suggest using await Task.Delay(1000); rather than Thread.Sleep(1000); as this is the canonical async way to idle.
For your code example:
class Program
{
static void Main(string[] args)
{
preform().Wait();
Console.ReadLine();
}
public static async Task Working()
{
Console.WriteLine("Please wait, the program is running");
}
public static async Task Takingtime()
{
Console.WriteLine("This Program started");
await Task.Delay(1000);
Console.WriteLine("The Program finished");
}
public static Task preform()
{
return Task.WhenAll(
Takingtime(),
Working());
}
}
Use Stephen Cleary's Nito.AsyncEx library (available via Nuget) to provide an asynchronous context for a console application. See here for more details.
Your application can then be written as ...
class Program
{
static int Main(string[] args)
{
try
{
Console.WriteLine("The application has started");
AsyncContext.Run(() => LongRunningTaskAsync(args));
Console.WriteLine("The application has finished");
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
return -1;
}
}
static async Task LongRunningTaskAsync(string[] args)
{
Console.WriteLine("The long running task has started");
// use Task.Delay() rather than Thread.Sleep() to avoid blocking the application
await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
Console.WriteLine("The long running task has finished");
}
}
Use preform().Wait() in the constructor. So the call is awaited. Then use await Takingtime() and await Working() in the preform method.
And you have to change the return type to Task.

Understanding async behaviour of C# console TCP server

I want to create a simple TCP server in .NET Core 2.0 using asynchrony (because from what I understand, it's more reasonable than spawning threads) with the async/await approach (because I believe it's more up-to-date than the one with IAsyncResult and *Begin/*End methods).
I've written this small server that accepts new connections from clients and then begins to send them 100 messages (with a 1s delay between them).
The main question is:
If I'm not spawning new threads, then how the server continues to send delayed messages to several clients, when in fact it's "waiting for connection"? Are there any hidden low-level signals/events involved, or are there really just new threads?
The second question is:
If I'm not using this brand new async Main syntax sugar and I'm not "awaiting" the async task of sending the messages -- am I using the asynchrony correctly?
class Program
{
public static void Main(string[] args)
{
StartServer();
}
public static void StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = listener.AcceptTcpClient(); // synchronous
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // not awaited -- StartServer is not async
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
await stream.WriteAsync(msg, 0, msg.Length); // returning back to caller?
await Task.Delay(1000); // and what about here?
}
client.Close();
}
}
What is the difference between the original code and the version below? What difference does async Main make?
class Program
{
public static async Task Main(string[] args)
{
await StartServer();
}
public static async Task StartServer()
{
IPAddress localhost = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(localhost, 5567);
Console.WriteLine($"Starting listening on {listener.Server.LocalEndPoint}");
listener.Start();
while (true)
{
Console.WriteLine("Waiting for connection...");
var client = await listener.AcceptTcpClientAsync(); // does it make any difference when done asynchronously?
Console.WriteLine($"Connected with {client.Client.RemoteEndPoint}!");
Console.WriteLine("Starting sending messages...");
SendHundredMessages(client); // cannot await here, because it blocks next connections
}
}
public static async Task SendHundredMessages(TcpClient client)
{
var stream = client.GetStream();
for (int i=0; i<100; i++)
{
var msg = Encoding.UTF8.GetBytes($"Message no #{i}\n");
var result = stream.WriteAsync(msg, 0, msg.Length);
await Task.Delay(1000);
await result;
}
client.Close();
}
}
The answer to the main question:
In the background, as a rule, objects work with some api(winapi for windows). These api can implement asynchrony differently. For example, events(winapi events) or callbacks. So the answer is yes - there are hidden signals or threads. For example, you can see the Ping class. Ping.InternalSend using ThreadPool.RegisterWaitForSingleObject for the task of asynchrony.
The answer about async Main:
In your first code because StartServer is not async the Main method will not get back control until your "accept" cycle ends.
In your second code, the Main method will get back control then listener.AcceptTcpClientAsync invoked. But because you using await StartServer(); the Main method will be wait until StartServer ends.
Some code for to explain:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
// not await for exploration
var task = StartServer();
Console.WriteLine("Main StartServer finished");
task.Wait();
Console.WriteLine("task.Wait() finished");
Console.ReadKey();
}
static async Task StartServer()
{
Console.WriteLine("StartServer enter");
for(int i = 0; i < 3; i++) {
await Task.Delay(1000); // listener.AcceptTcpClientAsync simulation
SendHundredMessages();
}
Console.WriteLine("StartServer exit");
}
static async Task SendHundredMessages()
{
Console.WriteLine("SendHundredMessages enter");
await Task.Run(() => {
Thread.Sleep(2000);
});
Console.WriteLine("SendHundredMessages exit");
}
}
}
This code generate this output:
StartServer enter
Main StartServer finished
SendHundredMessages enter
SendHundredMessages enter
SendHundredMessages exit
SendHundredMessages enter
StartServer exit
task.Wait() finished
SendHundredMessages exit
SendHundredMessages exit
As you can see, the Main method continued execution right after first Task.Delay.
A warning:
You do not wait end of SendHundredMessages, and this is very bad. In example output you can see that "SendHundredMessages ending" after "task.Wait() finished". In example application of course it not danger, but in real project you can get big problem.

Asyncronous console with task controlling

Please help me find the right solution.
The main problem the waiting completion of the program through the console, and at the same time monitor tasks.
I wrote some prototype, but I am not sure that is effective - in this approach, we spend an extra thread of waiting for action from the console. I do not see alternatives, since Console does not support asynchronous (some sort of Console.ReadLineAsync).
UPDATE:
I have two working Tasks (task1, task2).They simulate some real work.
The program is a console. So we need to give user a chance to stop the program.By default in consoles, this is done via the expectation of pressing "Enter" (through consoleTask).
The question is. How to wait for the completion of worker threads AND Monitor stop command from the user.
static void Main(string[] args)
{
CancellationTokenSource mycts = new CancellationTokenSource();
var task1 = Task.Run(() =>
{
// doing some work, that can throw exception
Thread.Sleep(1000);
// how to avoid this closuring ?
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
var task2 = Task.Run(() =>
{
// doing some work, that can throw exception
Thread.Sleep(5000);
// again closuring
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
// I do not know how to do better with Console !!
var consoleTask = Task.Factory.StartNew((cts) =>
{
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}, mycts).ContinueWith((_) => mycts.Cancel()); // Do I need caching this task?
// Waiting for the Completion or Exception
Task.WaitAny(task1, task2, consoleTask);
// Now waiting for the completion of workflow
try
{
Task.WaitAll(task1, task2);
}
catch (Exception ex)
{
// log faulted tasks
}
//Exit
}
There's a few guidelines you should follow:
Do not use ContinueWith. Use await instead.
Do not use Task.Factory.StartNew. Use Task.Run instead.
Do not mix blocking and asynchronous code. In the case of a console application, it's generally best to just have Main call a MainAsync and wait on the returned task. For most applications, that's the only blocking you should use.
I'm not sure what how to avoid this closuring? means.
In the case of ReadLine (and other Console methods), you are correct, there are unfortunately no asynchronous methods. It might work to use a separate thread, but the Console class (more particularly, the Console input and output streams) have some unusual locking going on under the covers, so I'm not positive this would work:
static void Main(string[] args)
{
MainAsync().Wait();
}
static CancellationTokenSource mycts = new CancellationTokenSource();
static async Task MainAsync()
{
try
{
var task1 = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => SomeWorkThatCanThrowException()));
var task2 = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => OtherWorkThatCanThrowException()));
var consoleTask = CancelAfterSuccessfulCompletionAsync(
Task.Run(() => MonitorConsole()));
await Task.WhenAll(task1, task2, consoleTask);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void OtherWorkThatCanThrowException()
{
Thread.Sleep(5000);
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}
static void SomeWorkThatCanThrowException()
{
Thread.Sleep(1000);
mycts.Token.ThrowIfCancellationRequested();
throw new InvalidOperationException("test");
}
static void MonitorConsole()
{
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
static async Task CancelAfterSuccessfulCompletionAsync(Task task)
{
await task;
mycts.Cancel();
}
Since console doesn't have a SynchronizationContext there's not much you can do without blocking the main thread while your async operations are executing.
However, it's much simpler if you just write your code as if it's asynchronous and block in the simplest possible way. I would suggest moving all your code into an async MainAsync and blocking once:
static void Main()
{
MainAsync().Wait();
}
static async Task MainAsync()
{
// manage tasks asynchronously
}
What you can do, instead of blocking, is use a custom context for executing asynchronous operations like Stephen Cleary's AsyncContext. This allows you to avoid blocking synchronously on a Task:
static void Main()
{
AsyncContext.Run(MainAsync);
}

WCF client blocks on async methods

I'm working on WCF client app, and I facing difficulties with the await/async pattern.
It seems that the line:
await client.LongOperationAsync();
always blocks. As I understand, the thread supposed to exit and continue on to the Main() method and then return when the async method completes, maybe I'm wrong.
The output for the code below is (always):
Test() started
Test() error
*
*
*
...
The Test() method always completes before the context returns to main. Any thoughts would be highly appreciated.
static void Main(string[] args)
{
Program p = new Program();
p.Test();
while (true)
{
Console.WriteLine("*");
Thread.Sleep(500);
}
}
private async Task Test()
{
Console.WriteLine("Test() started");
try
{
MySoapClient client = new MySoapClient(
new BasicHttpBinding(new BasicHttpSecurityMode()),
new EndpointAddress("http://badaddress"));
await client.LongOperationAsync();
Console.WriteLine("Test() success");
}
catch (Exception)
{
Console.WriteLine("Test() error");
return;
}
Console.WriteLine("Test() end successfully");
}
Async methods execute synchronously until the first await; if your LongOperationAsync method performs a blocking operation before its first await, the calling method will be blocked as well. I suspect that's what happening in your case.
This is probably because WebRequest.BeginGetResponse performs some of its work synchronously. See Stephen Toub's answer to this question:
The Async CTP's GetRequestStreamAsync and GetResponseAsync are simple
wrappers around the existing HttpWebRequest.BeginGetRequestStream and
BeginGetResponse in .NET 4. Those Begin* methods have a lot of setup
work they do (e.g. proxy, DNS, connection pooling, etc.) before they
can submit a request, and unfortunately today that work all happens
synchronously as part of the Begin* call.
In this case, you provided a bad domain name, so I suspect it takes a while for the DNS resolution to fail.

Categories