Benutzer mit den meisten Antworten
WCF Task abbrechen

Frage
-
Hallo,
ich rufe an einem WCF Dienst eine Funktion mit folgender Signatur auf:
Task<MeinResponse> SearchAsync(meinParameter parameter);
Die Klasse, die den Zugriff auf den WCF Dienst kappselt verfügt über nachstehende Methode:
public async Task<MeinResponse> ContactsAsync(MeinParameter parameter, CancellationToken cancellationToken) { }
Mir ist bekannt, wenn ich einen Task erzeuge, dass ich ein CancellationToken übergeben kann, an dessen CancellationTokenSource die Methode Cancel aufgerufen werden kann. Von der WCF Schnittstelle bekomme ich bereits einen konstruierten Task. Kann ich dennoch irgendwie das CancellationToken dem Task zuordnen, damit ich benachrichtigt werde, wenn cancellationTokenSource.Cancel aufgerufen wurde?
Antworten
-
Hallo,
ich habe es in meinem WCF Client wie folgt gelöst:
cancellationToken.Register(() => /*Aufruf einer Cancel-Methode am WCF Dienst*/);
Das war alles.
Viele Grüße,
Christian- Als Antwort markiert Christian315 Mittwoch, 7. August 2013 07:25
Alle Antworten
-
Hallo Christian,
async/await haben nur für den C#-Compiler eine Bedeutung. Du könntest mit der neuen Syntax höchstens erreichen, dass Du die generierten [XXX]Async()-Methoden im WCF-Client elleganter aufrufen kannst. Der Dienst würde von deinem CancellationToken allerdings nichts mitkriegen. Zum einen ist mit den generierten asynchronen WCF-Client-Methoden nicht auch gesagt, dass der Dienst ebenfalls asynchron implementiert ist (und überhaupt was mit dem Token anfangen kann), zum anderen müsste zwischen Client und Dienst eine Session aufrechterhalten werden, was sich in der Realität schwierig gestaltet und schlecht skaliert.
Gruß
Marcel -
Hi Marcel,
vielen Dank für Deine Hilfe!
Ich kann an dem WCF Dienst explizit eine Cancel Methode aufrufen.
Mir geht es nur darum mitzubekommen, wenn der User eine abgesetzte Abfrage abbrechen möchte. Dann kann ich am WCF Dienst die Anfrage abbrechen. Was ich gesehen habe, muss das CancellationToken bei der Konstruktion des Task mit übergeben werden. Da ich von dem WCF Dienst bereits ein fertigen Task bekomme, kann ich diesem das CancellationToken nicht zuordnen und ggf. die Exception abfangen, wenn cancellationTokenSource.Cancel() aufgerufen wird.
Ich habe mich gedanklich an nachstehendem Beispiel orientiert.
var t = new Task( () => { int i = 0; while (true) { i++; Thread.Sleep(1000); cancellationToken.ThrowIfCancellationRequested(); } }, cancellationToken ); t.Start(); Thread.Sleep(3000); WriteLine("communicate cancel request"); // request for the task to cancel cancellationTokenSource.Cancel(); try { t.Wait(); } catch (Exception e)
Hast Du eine Idee, wie ich dennoch mitbekomme, wenn der User cancellationTokenSource.Cancel() aufruft?
Viele Grüße,
Christian -
Hallo Christian,
Mir geht es nur darum mitzubekommen, wenn der User eine abgesetzte Abfrage abbrechen möchte. Dann kann ich am WCF Dienst die Anfrage abbrechen. Was ich gesehen habe, muss das CancellationToken bei der Konstruktion des Task mit übergeben werden. Da ich von dem WCF Dienst bereits ein fertigen Task bekomme, kann ich diesem das CancellationToken nicht zuordnen und ggf. die Exception abfangen, wenn cancellationTokenSource.Cancel() aufgerufen wird.
Async-Methoden geben ein Task<T> und keine "Future/Promise"-Klasse zurück, weil die Architekten von async/await vorhandene Infrastruktur verwenden und keine tautologischen neuen Klassen einführen wollten. Sie brauchten einfach einen Platzhaltertyp für ein später angeliefertes Ergebnis und da eignete sich der Task<T> aus der TPL recht gut.
Es ist aber wichtig festzustellen, dass mit async/await nicht alle Fähigkeiten eines Task<T> unterstützt werden, u.a. auch nicht das Abbrechen über CancellationTokenSource in der WCF.
Ein WCF-Dienst unterstützt nach außen nur die im Vertrag angegebenen Operationen, nichts weiter. Und da ist zwar eine ContactsAsync()-Methode vorhanden, aber keine Methode die die asynchrone Operation unterbrechen könnte.Der Aufruf von cts.Cancel() im Client modifiziert nur lokales State; der Client-Aufruf schafft es also nicht "über den Draht", weil es kein operatives Gegenstück auf der Dienstseite gibt.
Ich könnte mir aber eine CancelAsync()-Methode im Dienstvertrag vorstellen, die einfach ein Flag im gemeinsamen dienstseitigen State setzt (Session vorausgesetzt). In der ContactAsync()-Schleife wird dann das Flag überprüft und die langzeitige Dienst-Operation ggf. unterbrochen.
Dieses Vorgehen würde funktionieren und würde in etwa dem ereignisbasierten asynchronen Muster entsprechen.
Gruß
Marcel- Bearbeitet Marcel RomaModerator Sonntag, 4. August 2013 09:02
-
Hi Marcel,
vielen Dank für Deine klasse Hilfe!
Der Aufruf von cts.Cancel() im Client modifiziert nur lokales State; der Client-Aufruf schafft es also nicht "über den Draht", weil es kein operatives Gegenstück auf der Dienstseite gibt.
Das ist kein Problem. Mir geht es nur darum am Client mitzubekommen, ob cts.Cancel() aufgerufen wurde. Müsste ich einen eigenen Task erzeugen und diesem das cancellationToken mitgeben, wobei dieser Task die ContactsAsync aufruft, d.h. es gäbe verschachtelte Tasks?
Viele Grüße,
Christian -
Hallo,
ich habe es in meinem WCF Client wie folgt gelöst:
cancellationToken.Register(() => /*Aufruf einer Cancel-Methode am WCF Dienst*/);
Das war alles.
Viele Grüße,
Christian- Als Antwort markiert Christian315 Mittwoch, 7. August 2013 07:25
-
Hallo Christian,
interessant dass cancellationToken.Register(action) anscheinend funktioniert, während Task.ContinueWith(action, TaskContinuationOptions.OnlyOnCanceled) das nicht tut. Wie erklärst Du dir das?
Wird die dienstseitige Operation die ja noch "in progress" ist und zunächst nur ein Promise zurückgegeben hat auch wirklich abgebrochen?
Nachfolgend ein Auszug aus einem Ausführungsprotokoll bei mir, der mir ganz andere Sachen nahelegt.
CLIENT (CLN)
12:08:17.543 CLN: ContactsClient running... 12:08:17.558 CLN: Starting new cancellable contact task ... 12:08:17.558 CLN: Awaiting Contacts... 12:08:20.070 CLN: Timer task fired: Client cancels task by calling cts.Cancel() 12:08:20.070 CLN: Registered Cancel()-callback executed. 12:08:22.894 CLN: Got Contacts. 1st Contact 2nd Contact 3rd Contact 4th Contact Last Contact
DIENST (SVC)
Service instantiated, hosted, and running. Press any key to stop. 12:08:17.761 SVC: Entering ContactsAsync() 12:08:17.761 SVC: Session-ID is: 5bcb0c46-353b-4ea0-a6be-366bb15c39d3 12:08:17.761 SVC: Adding two contacts to the return list... 12:08:17.761 SVC: More contacts will be added to the return list after timer is triggered... 12:08:17.777 SVC: Setting Up Timer... 12:08:17.777 SVC: Returning placeholder response... 12:08:22.784 SVC: Timer triggered... 12:08:22.784 SVC: Adding more contacts to the return list... 12:08:22.784 SVC: Added all contacts to the return list... 12:08:22.816 SVC: Cancel()-request reached the service... 12:08:22.816 SVC: Session-ID is: 5bcb0c46-353b-4ea0-a6be-366bb15c39d3 12:08:22.816 SVC: Calling TaskCompletionSource.TrySetCanceled() 12:08:22.816 SVC: TaskCompletionSource.TrySetCanceled() succeeded: False
Deswegen meine Rückfrage an Dich: Wie bricht man denn die Bearbeitung dienstseitig ab bei einem WCF-Dienst, der die Kontakte über eine asynchrone Request/Reply-Methode zurückgibt?
Das obige Protokoll scheint zu sagen, dass das nicht geht, da die Operations-Anfragen in einer Session sequentiell ankommen und somit die Cancel-Anforderung, auch wenn sie vom Client abgesetzt werden kann, erst nach Beendigung der vorherigen awaitbaren Operation verarbeitet werden kann.
Gruß
Marcel -
Hi Marcel,
ich habe die Vermutung, dass ich nicht alle Informationen genannt habe.
Es gibt zwei Schnittstellen. Eine Client-Schnittstelle und eine WCF-Schnittstelle.
Für die Client-Schnittstelle gibt es eine Implementierung, die von Client-Anwendungen, z.B. WPF oder WinForm genutzt werden kann. Die Client-Schnittstelle sieht sinngemäß so aus:
Task<MeinResponse> SucheAsync(MeineSucheAnfrage anfrage, CancellationToken cancellationToken, IProgress<List<EinKontakt>> progress);
Im WCF-Dienst wird ein Job gestartet, welcher abgebrochen werden kann. Hierfür bietet der WCF-Dienst eine Methode anasync Task CancelAsync(Guid idDerAnfrage)
In der Implementierung der Cient-Schnittstelle habe ich lediglich die Information gebraucht, wann der Benutzer die laufende Suche abbrechen wollte.
Task<MeinResponse> SucheAsync(MeineSucheAnfrage anfrage, CancellationToken cancellationToken, IProgress<List<EinKontakt>> progress); { cancellationToken.Register(async () => { // -> Hier WCF-Aufruf für den Abbruch await wcfService.Abbruch(anfrage.ID); }); // -> Hier WCF Aufruf für die async Suche im WCF Dienst await wcfService.SucheAsync(...)
D.h., es gibt keine durchgängige Abbruch-Benachrichtigung mittels CancellationToken an dem WCF-Service.
Sorry.
Christian