Ich bin neu bei C#
s await/async
und spiele derzeit etwas herum.
In meinem Szenario habe ich ein einfaches Client-Objekt, das eine WebRequest
name__-Eigenschaft hat. Der Client sollte regelmäßig Live-Nachrichten über den WebRequest
name__s RequestStream
name __. Senden. Dies ist der Konstruktor des Client-Objekts:
public Client()
{
_webRequest = WebRequest.Create("some url");
_webRequest.Method = "POST";
IsRunning = true;
// --> how to start the 'async' method (see below)
}
und die Async-Alive-Sender-Methode
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++;
}
}
Wie soll die Methode gestartet werden?
neuer Thread (SendAliveMessageAsync) .Start ();
oder
Task.Run (SendAliveMessageAsync); // Ändern des zurückgegebenen Typs in Task
oder
erwarten Sie SendAliveMessageAsync (); // schlägt fehl, da der Konstruktor nicht asynchron ist
Meine Frage bezieht sich mehr auf mein persönliches Verständnis von await/async
, was in einigen Punkten falsch sein könnte.
Die dritte Option ist das Werfen
The 'await' operator can only be used in a method or lambda marked with the 'async' modifier
Wie soll die Methode gestartet werden?
Ich stimme für "keine der oben genannten". :)
"Feuer und Vergessen" ist ein schwieriges Szenario, das richtig gehandhabt werden kann. Insbesondere die Fehlerbehandlung ist immer problematisch. In diesem Fall kann async void
Sie überraschen.
Ich ziehe es vor, die Aufgaben explizit zu speichern, wenn ich sie nicht sofort await
ing:
private async Task SendAliveMessageAsync();
public Task KeepaliveTask { get; private set; }
public Client()
{
...
KeepaliveTask = SendAliveMessageAsync();
}
Dies ermöglicht es zumindest den Verbrauchern von Client
, Ausnahmen zu erkennen und wiederherzustellen, die von der SendAliveMessageAsync
-Methode ausgelöst werden.
Nebenbei bemerkt entspricht dieses Muster fast meinem "asynchronen Initialisierungs" -Muster .
Da die vorherige Antwort falsch war, wurde sie bearbeitet:
Da es sich im Konstruktor befindet, denke ich, müsste man einen neuen Thread dafür erstellen. Das würde ich persönlich mit machen
Task.Factory.StartNew (() => SendAliveMessageAsync ());
Da es sich um eine Feuer- und VergissOperation handelt, sollten Sie es mit verwenden
SendAliveMessageAsync();
Beachten Sie, dass await
keine neue Task
startet. Es ist nur syntaktischer Zucker, zu warten, bis eine Task
abgeschlossen ist.
Mit Task.Run
wird ein neuer Thread gestartet.
In SendAliveMessageAsync
solltest du also eine neue Aufgabe starten:
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++;
}
});
}
Hier ist eine Option, da Sie await
nicht innerhalb eines Konstruktors aufrufen können.
Ich würde vorschlagen, das Reactive Framework von Microsoft (NuGet "Rx-Main") zu verwenden.
Der Code würde so aussehen:
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();
}
}
Dieser Code wickelt alle erforderlichen Threading-Prozesse ab und gibt die Variable StreamWriter
ordnungsgemäß ab.
Wann immer Sie die Keep Alives stoppen möchten, rufen Sie einfach _subscription.Dispose()
auf.
In Ihrem Code müssen Sie nicht async/await verwenden. Richten Sie einfach einen neuen Thread ein, um eine lange Operation auszuführen.
private void SendAliveMessage()
{
const string keepAliveMessage = "{\"message\": {\"type\": \"keepalive\"}}";
var sreamWriter = new StreamWriter(_webRequest.GetRequestStream());
while (IsRunning)
{
sreamWriter.WriteLine(keepAliveMessage);
Thread.Sleep(10 * 1000);
}
}
Verwenden von Task.Factory.StartNew(SendAliveMessage, TaskCreationOptions.LongRunning)
zum Ausführen der Operation.
Wenn Sie wirklich async/await pattern verwenden möchten, rufen Sie es einfach im Konstruktor auf, ohne Modifizierer und den Vergessen-Vorgang zu erwarten.
public Client()
{
_webRequest = WebRequest.Create("some url");
_webRequest.Method = "POST";
IsRunning = true;
SendAliveMessageAsync(); //just call it and forget it.
}
Ich denke, es ist keine gute Idee, einen langen Thread oder async/await-Pattern zu erstellen. Timer sind in dieser Situation möglicherweise besser geeignet.