Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Event aus asyncroner Methode
ill_son
myCSharp.de - Member



Dabei seit:
Beiträge: 188
Herkunft: Leipzig

Themenstarter:

Event aus asyncroner Methode

beantworten | zitieren | melden

Hallo,

folgendes Problem bekomme ich gerade nicht gelöst:

Ich habe eine Klasse, welche mir über VCP an den Rechner angeschlossene Sensoren sucht. Diese hat folgende Methode


public async Task ScanAsync(IProgress<NewSensor> progressIndicator)
{
    if (!IsBusy)
    {
        IEnumerable<string> portNames = _ComPortWatcher.GetCurrentPortNames();
        await ScanPortsForSensors(portNames, progressIndicator);
    }
}

Diese starte ich aus meinem VM mit einem AsyncCommand -> alles gut.

Nun soll aber der Scan auch ausgelöst werden, wenn neue Com-Ports auftauchen. Dafür gibt es eine Klasse, die auf entsprechende RegistryEvents reagiert und einen Scan anstößt.


private async void ComPortWatcher_PortsAdded(object sender, SerialCommEventArgs e)
{
    await ScanPortsForSensors(e.PortNames, null); 
}

Nun meine Frage: Wie bekomme ich die detektierten Sensoren gemeldet? Wenn ich es richtig verstanden habe kehrt ja der await-Aufruf sofort zurück (fire and forget), da ComPortWatcher_PortsAdded async void ist und nicht Task zurück gibt, was bei Eventhandlern ja legitim zu sein scheint, wie ich gelesen habe. Kann ich da im Anschluss ein Event auslösen?

Grüße, Alex
Final no hay nada más
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1495
Herkunft: Düsseldorf

beantworten | zitieren | melden

Das await ist im Hintergrund etwas anders, als Du denkst.
In deinem Fall wird das await vermutlich alles darauf Folgende in den UI-Thread zur Bearbeitung geben.
Das await kehrt dabei nicht fire-and-forget zurück! Dass kein Task zurückgegeben wird, heißt nur, dass der Methodenaufrufer nicht auf den Task warten kann, aber das ist bei Events legitim.

Das heißt, Du kannst nach dem Await normal ein Event aufrufen.
private Nachricht | Beiträge des Benutzers
JimStark
myCSharp.de - Member

Avatar #dOpLzh7hN1az1g0eGRc0.jpg


Dabei seit:
Beiträge: 284

beantworten | zitieren | melden

Soll deine zweite Klasse dann das Event auslösen? (Also nicht ComPortWatcher?)

Wenn ja, füg ein Delegate hinzu, definier dein Event und abonniere es dann wo es nutzen willst.
In die PortsAdded-Methode kommt dann das Invoke (wenn ich dein Vorhaben richtig verstanden habe)
private Nachricht | Beiträge des Benutzers
ill_son
myCSharp.de - Member



Dabei seit:
Beiträge: 188
Herkunft: Leipzig

Themenstarter:

beantworten | zitieren | melden

Hallo,

danke für eure Antworten.

@JimShark: So habe ich es im Augenblick auch gemacht. Ich habe es noch ein bisschen umgeschrieben, um den Progress auch nur dort zu benutzen, wo ich ihn brauche.


private async Task<IEnumerable<NewSensor>> ScanPortsForSensorsAsync(IEnumerable<string> portNames)
{
    // scan goes here
}

private async void ComPortWatcher_PortsAdded(object sender, SerialCommEventArgs e)
{
    IEnumerable<NewSensor> newSensors = await ScanPortsForSensorsAsync(e.PortNames);
    foreach (NewSensor sensor in newSensors)
    {
        NewSensorDetected?.Invoke(this, new NewSensorEventArgs(sensor));
    }
}

Der SensorScanner aboniert das PortsAdded-Event des ComPortWatchers und löst daraufhin einen Scan aus. Die Frage war nur, ob das so einfach geht. Ich bin noch einbisschen neu in der Thematik async Task und alles was ich immer gelesen habe, war: Task mit Progress verwenden. Neulich hatte ich ein interessantes Tutorial, wo erklärt wurde, dass async void wie fire-and-forget zu betrachten ist. Momentan läuft es auch noch nicht zuverlässig.
Final no hay nada más
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1495
Herkunft: Düsseldorf

beantworten | zitieren | melden

Führe die Events doch direkt in der Methode aus, oder gibt es einen konkreten Grund, warum Du das nicht machst?
Die Methode ist nicht nur wegen des awaits automatisch in einem neuen Thread, für die Methode gilt intern das gleiche, wie für das Event.
Den neuen Thread hast Du erst dann, wenn Du auch tatsächlich einen neuen Task auf machst, der dann einen neuen Thread bekommt.

Mach dir Mal in VisualStudio eine Watch auf auf die aktuelle ThreadID oder schreib dir ThreadID ins Debug-Output und verfolge sie.
Zitat
Task mit Progress verwenden
Das Progress-System ist nur ein Konzept, Status-Änderungen abstrahiert an die UI weiterzugeben.
Du musst kein Progress nutzen, aber Du kannst. Nutzen würde ich es nur dann, wenn ich es auch brauche.
Zitat
dass async void wie fire-and-forget zu betrachten ist
Der Aufruf von einer Methode mit async void ist wie fire-and-forget, die Methode selber nicht.


// EventHandler sind seltene Ausnahmen, wo async void in Ordnung ist:
async void OnMyEvent(object sender, EventArgs e)
{
    // UI-Thread
    await DoWorkAsync();
    // DoWorkAsync ist zuende
    // UI-Thread
}

async Task DoWorkAsync()
{
    // UI-Thread
    await Task.Delay(100);
    // Task.Delay ist zuende
    // UI-Thread
}

// Fast immer schlecht:
async void DoAsyncVoidWork()
{
    // UI-Thread
    await DoWorkAsync();
    // DoWorkAsync ist zuende
    // UI-Thread
}

void DoOtherWork()
{
    // UI-Thread
    DoAsyncVoidWork(); // Fire & Forget
    // await DoAsyncVoidWork(); // Compile-fehler
    // DoWorkAsync ist NICHT zuende
    // UI-Thread
}

Du solltest dich ausführlich mit Tasks beschäftigen.
Für kleine Projekte ok, solange Fehler nicht so schlimm sein, aber im produktiven Umfeld können die schnell zu schlimmen Problemen werden.
Die Einstiegshürde ist leider ziemlich groß, dafür machen sie einige Probleme bedeutend leichter, wenn man weiß, was man tut.
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von Palladin007 am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15967

beantworten | zitieren | melden

Zitat von ill_son
Neulich hatte ich ein interessantes Tutorial, wo erklärt wurde, dass async void wie fire-and-forget zu betrachten ist.
Konzeptionell ist dem so - kommt aber auf den Kontext an.
In diesem Fall vermute ich, dass Du es falsch verstehst.

async void führt dazu, dass der Aufrufer keinerlei Informationen über die Aktion erhält; eben keinen Task.
Aus Sicht des Aufrufers ist damit dieser Aufruf "Fire and Forget".

Inhaltlich ist das aber kein Fire and Forget, denn in der Methode selbst existiert die State Machine und wird auch beachtet.

Ein echtes technologisches Fire and Forget geht entweder über Task.Run (die bequeme Methode) bzw. absolut korrekt über ThreadPool.UnsafeQueueUserWorkItem()

PS: ein Task sollte kein IEnumerable zurück geben.
Entweder Du arbeitest mit materialisierten Listen wie List<T> oder Du nimmst IAsyncEnumerable
IEnumerable kann unerwünschte Seiteneffekte hervorrufen und ist selbst auch überhaupt nicht asynchron.

PPS: da Du eine asynchrone UI offenbar umsetzen willst, solltest Du Dir mal Reactive Extensions anschauen.
Damit lassen sich 99% der UI Szenarien viel einfacher umsetzen, hast es sauber asynchron, kannst Bindings verwenden und musst Dir über nervige Events keine sorgen machen, weil Du einfach Subscriptions zur Aktualisierung der Inhalte verwenden kannst.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers