Async WCF selbst gehosteter Dienst

8

Mein Ziel ist es, einen asynchronen, selbst gehosteten WCF-Dienst zu implementieren, der alle Anforderungen in einem einzigen Thread ausführt und die neuen C # 5-Async-Features voll ausnutzt.

Mein Server wird eine Konsolen-App sein, in der ich ein SingleThreadSynchronizationContext einrichten werde, wie angegeben hier , erstellen und öffnen Sie ein ServiceHost Führen Sie anschließend SynchronizationContext aus, sodass alle WCF-Anforderungen im selben Thread behandelt werden.

Obwohl der Server alle Anforderungen im selben Thread erfolgreich verarbeiten konnte, blockieren async-Vorgänge die Ausführung und werden serialisiert, anstatt interlaced zu werden.

Ich habe eine vereinfachte Probe vorbereitet, die das Problem reproduziert.

Hier ist mein Servicevertrag (der gleiche für Server und Client):

%Vor%

Die Implementierung des Dienstes ist die folgende (sie ist ein wenig vereinfacht, aber die endgültige Implementierung kann auf Datenbanken zugreifen oder sogar andere Dienste asynchron aufrufen):

%Vor%

Der Dienst wird in einer Konsolenanwendung gehostet:

%Vor%

Wie Sie sehen können, ist das erste, was ich tue, einen einzelnen Thread SynchronizationContext einzurichten. Anschließend erstelle, konfiguriere und öffne ich einen ServiceHost. Laut diesem Artikel , wie ich den SynchronizationContext vor seiner Erstellung festgelegt habe, die ServiceHost erfasst es und alle Client-Anfragen werden in SynchronizationContext veröffentlicht. In der Sequenz starte ich SingleThreadSynchronizationContext im selben Thread.

Ich habe einen Test-Client erstellt, der den Server in einer "Fire-and-Forget" -Methode aufruft.

%Vor%

Wenn ich das Beispiel ausführe, bekomme ich folgende Ergebnisse:

Kunde

Server

Die Nachrichten werden vom Client mit einem minimalen Intervall (& lt; 1s) gesendet. Ich erwartete, dass der Server den ersten Aufruf erhalten würde, und führte ihn in SingleThreadSynchronizationContext aus (ein neues WorkItem einreihen. Wenn das await Schlüsselwort erreicht war, wurde SynchronizationContext noch einmal erfasst, die Fortsetzung wurde gepostet), und die Methode würde an dieser Stelle eine Aufgabe zurückgeben, die SynchronizationContext freigibt, um mit der zweiten Anfrage fertig zu werden (zumindest damit anzufangen).

Wie Sie anhand der ID des Threads im Serverprotokoll sehen können, werden die Anforderungen korrekt in SynchronizationContext gepostet. Betrachtet man jedoch die Zeitstempel, können wir sehen, dass die erste Anfrage abgeschlossen wird, bevor die zweite gestartet wird, was den Zweck eines asynchronen Servers völlig zunichte macht.

Warum passiert das?

Welcher Art ist die Implementierung eines selbst gehosteten asynchronen WCF-Servers?

Ich denke, das Problem ist mit dem SingleThreadSynchronizationContext, aber ich kann nicht sehen, wie man es auf andere Weise implementiert.

Ich habe das Thema recherchiert, aber ich konnte keine nützlicheren Informationen zum asynchronen WCF-Service-Hosting finden, insbesondere nicht mit dem aufgabenbasierten Muster.

ZUSATZ

Hier ist meine Implementierung von SingleThreadedSinchronizationContext . Es ist im Grunde das gleiche wie das im Artikel :

%Vor%     
Arthur Nunes 13.06.2013, 05:49
quelle

1 Antwort

9

Sie müssen ConcurrencyMode.Multiple anwenden.

Dies ist, wo die Terminologie ein wenig verwirrend wird, weil es in diesem Fall nicht wirklich "multi-threaded" bedeutet, wie der MSDN-Dokumentstatus. Es bedeutet gleichzeitig . Standardmäßig (einzelne Parallelität) verzögert WCF andere Anforderungen, bis die ursprüngliche Operation abgeschlossen ist. Daher müssen Sie mehrere gleichzeitige Operationen angeben, um überlappende (gleichzeitige) Anforderungen zuzulassen. Ihr SynchronizationContext garantiert weiterhin, dass nur ein einziger Thread alle Anfragen verarbeitet, also ist es nicht tatsächlich Multi-Threading. Es ist Single-Threaded Concurrency.

Als Nebenbemerkung möchten Sie vielleicht ein anderes SynchronizationContext mit einer saubereren Shutdown-Semantik betrachten. Das SingleThreadSynchronizationContext , das Sie gerade verwenden, wird "geklemmt", wenn Sie Complete aufrufen; Alle async -Methoden, die sich in await befinden, werden nie wieder aufgenommen.

Ich habe einen AsyncContext -Typ , der bessere Unterstützung für sauberes Herunterfahren bietet. Wenn Sie das Nito.AsyncEx NuGet-Paket installieren, können Sie den folgenden Servercode verwenden:

%Vor%

Dadurch wird Ctrl-C in einen "weichen" Exit umgewandelt, was bedeutet, dass die Anwendung so lange ausgeführt wird, wie Clientverbindungen bestehen (oder bis das Zeitlimit überschritten wird). Während des Schließens können bestehende Clientverbindungen neue Anforderungen stellen, neue Clientverbindungen werden jedoch abgelehnt.

Ctrl-Break ist immer noch ein "harter" Ausgang; Sie können das in einem Konsolenhost nicht ändern.

    
Stephen Cleary 13.06.2013, 13:15
quelle