web-dev-qa-db-de.com

Async-Methode als Thread oder als Task starten

Ich bin neu bei C#s await/async und spiele derzeit etwas herum.

In meinem Szenario habe ich ein einfaches Client-Objekt, das eine WebRequestname__-Eigenschaft hat. Der Client sollte regelmäßig Live-Nachrichten über den WebRequestname__s RequestStreamname __. 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
6
KingKerosin

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 awaiting:

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 .

7
Stephen Cleary

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

3
John Clifford

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++;
        }
    });
}
0
Domysee

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.

0
Enigmativity

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.

0
cFish