Running the async method as Thread or as Task

I am new to C# await/async and am currently playing a little.

In my scenario, I have a simple client object that has the WebRequest property. The client must periodically send live messages on the WebRequest RequestStream . This is the client object constructor:

 public Client() { _webRequest = WebRequest.Create("some url"); _webRequest.Method = "POST"; IsRunning = true; // --> how to start the 'async' method (see below) } 

and async live-sender method

 private async void SendAliveMessageAsync() { const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}"; var seconds = 0; while (IsRunning) { if (seconds % 10 == 0) { await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage); } await Task.Delay(1000); seconds++; } } 

How to start a method?

new topic (SendAliveMessageAsync) .Start ();

or

Task.Run (SendAliveMessageAsync); // change the return type in Task

or

Expect SendAliveMessageAsync (); // does not work because the constructor is not async

My question is about my personal understanding of await/async , which I think may be erroneous at some points.

The third option - throw

 The 'await' operator can only be used in a method or lambda marked with the 'async' modifier 
+3
source share
5 answers

How to start a method?

I vote for "none of the above." :)

โ€œFire and Forgetโ€ is a complex scenario for proper handling. In particular, error handling is always problematic. In this case, async void may surprise you.

I prefer to explicitly save tasks, if not immediately await them:

 private async Task SendAliveMessageAsync(); public Task KeepaliveTask { get; private set; } public Client() { ... KeepaliveTask = SendAliveMessageAsync(); } 

This, at least, allows Client consumers to detect and recover exceptions thrown by the SendAliveMessageAsync method.

On a side note, this pattern is almost equivalent to the asynchronous initialization pattern.

+5
source

Edited as the previous answer was incorrect:

As in the constructor, I think you have to deploy a new thread for it. I personally would do this using

Task.Factory.StartNew (() => SendAliveMessageAsync ());

+2
source

Since this is a fire and forget operation, you must start it with

 SendAliveMessageAsync(); 

Note that await does not start a new Task . It is just syntactic sugar awaiting the completion of the Task .
A new thread is launched using Task.Run .

So, inside SendAliveMessageAsync you have to start a new task:

 private async Task SendAliveMessageAsync() { const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}"; await Task.Run( () => { var seconds = 0; while (IsRunning) { if (seconds % 10 == 0) { await new StreamWriter(_webRequest.GetRequestStream()).WriteLineAsync(keepAliveMessage); } await Task.Delay(1000); seconds++; } }); } 
0
source

This is an option, since you cannot call await from within the constructor.

I would suggest using the Microsoft Reactive Framework (NuGet "Rx-Main").

The code will look like this:

 public class Client { System.Net.WebRequest _webRequest = null; IDisposable _subscription = null; public Client() { _webRequest = System.Net.WebRequest.Create("some url"); _webRequest.Method = "POST"; const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}"; var keepAlives = from n in Observable.Interval(TimeSpan.FromSeconds(10.0)) from u in Observable.Using( () => new StreamWriter(_webRequest.GetRequestStream()), sw => Observable.FromAsync(() => sw.WriteLineAsync(keepAliveMessage))) select u; _subscription = keepAlives.Subscribe(); } } 

This code processes all the necessary thread and correctly places the StreamWriter as it is used.

Whenever you want to stop the listing, you can simply call _subscription.Dispose() .

0
source

There is no need to use async / wait in your code, just set up a new thread for continuous operation.

 private void SendAliveMessage() { const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}"; var sreamWriter = new StreamWriter(_webRequest.GetRequestStream()); while (IsRunning) { sreamWriter.WriteLine(keepAliveMessage); Thread.Sleep(10 * 1000); } } 

Use Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning) to perform the operation.

If you really want to use the async / await pattern, just call it in the constructor without the wait modifier and forget it.

 public Client() { _webRequest = WebRequest.Create("some url"); _webRequest.Method = "POST"; IsRunning = true; SendAliveMessageAsync(); //just call it and forget it. } 

I think this is not a good idea - create a long stream or use the async / await template. Timers may be more appropriate in this situation.

0
source

Source: https://habr.com/ru/post/1268545/


All Articles