Ich habe eine grundlegende WCF-Konsolenserveranwendung erstellt. Mein Ziel ist es, mehrere Aufrufe parallel zu bearbeiten, aber die aktuelle Version behandelt sie sequenziell.Aufrufe von asynchronen WCF-Dienst, der sequenziell ausgeführt wird
Bitte tragen Sie mit mir, da es eine Wand von Code zu folgen, aber es ist ziemlich einfach Zeug. Nur schwer für mich zu isolieren, da es Teil einer ziemlich großen VS-Lösung ist.
Ich bin die Straße von TPL gegangen und async/erwarten Stichwort, das ich im Grunde verstehe und mag.
Die Service-Schnittstelle:
<ServiceContract()>
Public Interface IGetBackendData
<OperationContract>
Function SendRequest(request As Request) As Task(Of RequestResponse)
<OperationContract>
Function GetNextPackage(serverJobID As Guid) As Task(Of PackageBase)
End Interface
Der Proxy:
Public Class imBackendServerProxy
Inherits ClientBase(Of IGetBackendData)
Implements IGetBackendData
Public Function SendRequest(request As Request) As Task(Of RequestResponse) Implements IGetBackendData.SendRequest
Return Channel.SendRequest(request)
End Function
Public Function GetNextPackage(serverJobID As Guid) As Task(Of PackageBase) Implements IGetBackendData.GetNextPackage
Return Channel.GetNextPackage(serverJobID)
End Function
End Class
Und die Umsetzung:
Public Class GetDataService
Implements IGetBackendData
Private ActiveJobs As New Dictionary(Of Guid, ServiceJobBase)
Private Function ProcessRequest(request As Request) As RequestResponse
Dim newJob As ServiceJobBase
Select Case request.Command.CommandType
Case ImagiroQueryLanguage.CommandTypes.CommandHello
newJob = New HelloJob
Case Else
Throw New ArgumentException("Do not know how to process request")
End Select
If newJob IsNot Nothing Then
newJob.AssignedRequest = request
ActiveJobs.Add(newJob.ID, newJob)
Return newJob.GetResponse()
End If
Throw New ArgumentException("job could not be started")
End Function
Public Async Function SendRequest(request As Request) As Task(Of RequestResponse) Implements IGetBackendData.SendRequest
Console.WriteLine("Request recieved")
Dim mytask As Task(Of RequestResponse) = Task.Factory.StartNew(Function() ProcessRequest(request))
Await Task.Delay(1500)
Return Await mytask.ConfigureAwait(True)
End Function
Private Function GenerateNextPackage(jobid As Guid) As PackageBase
If Not ActiveJobs.ContainsKey(jobid) Then
Throw New ArgumentException("job could Not be found")
End If
Dim nextPackage As PackageBase = ActiveJobs(jobid).GetNextPackage()
If TypeOf (nextPackage) Is PackageEnd Then
ActiveJobs.Remove(jobid)
End If
Return nextPackage
End Function
Public Async Function GetNextPackage(serverTaskID As Guid) As Task(Of PackageBase) Implements IGetBackendData.GetNextPackage
Dim mytask As Task(Of PackageBase) = Task.Factory.StartNew(Of PackageBase)(Function() GenerateNextPackage(serverTaskID))
Await Task.Delay(1500)
Return Await mytask.ConfigureAwait(True)
End Function
End Class
A "Request" Objekt enthält ein "Befehl" Objekt (abgeleitet von CommandBase
) plus zusätzliche Informationen. Ein "Package" -Objekt (abgeleitet von PackageBase
) enthält die Daten, die vom Server zum Client übertragen werden.
Die Grundidee, wie die Kommunikation funktionieren soll wie folgt lautet:
1. "Request" phase
Client --Request--> Server
Client <--GUID A -- Server
2. "Data" phase
Client -- GUID A --> Server
Client <--DataOrStop-- Server
3. Repeat step 2. until Server says stop.
die Daten und Anforderungsantwort verbrauchen, ich die folgende Klasse haben:
Public Class DataReceiver
Public Event DataPackageRecieved(sender As Object, arg As DataPackageReceivedEventArgs)
Public Event EndOfTransmission(sender As Object, arg As EventArgs)
Public Sub New(response As RequestResponse, proxy As imBackendServerProxy, dataRecieved As DataPackageRecievedEventHandler, endOfTransmission As EndOfTransmissionEventHandler)
ID = response.JobID
p = proxy
AddHandler Me.DataPackageRecieved, dataRecieved
AddHandler Me.EndOfTransmission, endOfTransmission
FetchData()
End Sub
Public Property ID As Guid
Private p As imBackendServerProxy
Private Sub FetchData()
Dim t As Task(Of PackageBase) = Task.Factory.StartNew(Of PackageBase)(Function() p.GetNextPackage(ID).Result)
Debug.Print("Waiting for Result FetchData")
t.ContinueWith(AddressOf DoneFetching)
End Sub
Public Delegate Sub ProcessDataPackageDelegate(recievedDataPackage As PackageBase)
Public Property ProcessDataPackage As ProcessDataPackageDelegate
Private Sub DoneFetching(arg As Task(Of PackageBase))
If arg.IsCompleted Then
If TypeOf (arg.Result) Is PackageEnd Then
RaiseEvent EndOfTransmission(Me, Nothing)
Else
RaiseEvent DataPackageRecieved(Me, New DataPackageReceivedEventArgs With {.DataPackage = arg.Result})
FetchData()
End If
End If
End Sub
End Class
In meinem WPF Test Client Anwendung Ich habe einen Button, mit dem ich Anfragen an den Server senden kann. Die Klasse HelloCommand
(abgeleitet von CommandBase
) wird verwendet, um eine Ganzzahl "n" zum Server zu transportieren. Der Server antwortet dann auf jeden der folgenden Anrufe mit einem HelloPackage
(abgeleitet von PackageBase
) und schließlich mit einem EndPackage
(abgeleitet von PackageBase
).
Die Logik dafür wird in den ServiceJob-Objekten behandelt (abgeleitet von ServiceJobBase
) - grundsätzlich hat jedes "Command" -Objekt ein entsprechendes "ServiceJob" -Objekt, das wiederum entsprechende "Package" -Objekte an die sequentiellen Client-Anfragen sendet.
Da der Client die benötigte "Sequenzialität" der "Datenanforderungen" verarbeitet, d. H. Sequenzielle Aufrufe an die Funktion GetNextPackage
, werden diese Aufrufe niemals überlappen. Aber ich würde sehr gerne zwei oder mehr separate Sequenzen von Aufrufen an GetNextPackage
- und ihre jeweiligen "ServiceJobs" - auf dem Server parallel ausgeführt werden. Und das passiert nicht.
Fügen Sie einen einfachen Zähler zur HelloServiceJob
Klasse hinzu, um jede Anfrage einfach zu identifizieren. Ein Druck auf die Schaltfläche in meinem WPF-Client liefert die folgende Ausgabe auf dem Server, während die Benutzeroberfläche reaktionsfähig bleibt.
mit 1,5 Sekunden zwischen jeder Zeile wie erwartet.
Drei schnelle aufeinanderfolgende Druckvorgänge ergeben die folgende Ausgabe auf dem Server, während die Benutzeroberfläche reaktionsschnell bleibt.
Request recieved (1)
Request recieved (2)
Request recieved (3)
Sending HelloPackage - 6 remaining (1)
Sending HelloPackage - 6 remaining (2)
Sending HelloPackage - 6 remaining (3)
Sending HelloPackage - 5 remaining (1)
Sending HelloPackage - 5 remaining (2)
Sending HelloPackage - 5 remaining (3)
Sending HelloPackage - 4 remaining (1)
Sending HelloPackage - 4 remaining (2)
Sending HelloPackage - 4 remaining (3)
Sending HelloPackage - 3 remaining (1)
Sending HelloPackage - 3 remaining (2)
Sending HelloPackage - 3 remaining (3)
Sending HelloPackage - 2 remaining (1)
Sending HelloPackage - 2 remaining (2)
Sending HelloPackage - 2 remaining (3)
Sending HelloPackage - 1 remaining (1)
Sending HelloPackage - 1 remaining (2)
Sending HelloPackage - 1 remaining (3)
Sending HelloPackage - 0 remaining (1)
Sending HelloPackage - 0 remaining (2)
Sending HelloPackage - 0 remaining
Sending no more HelloPackages (1)
Sending no more HelloPackages (2)
Sending no more HelloPackages (3)
Während die Reihenfolge erwartet wird, nimmt jede Zeile 1,5 Sekunden der Client und der Server immer nur auf Nachricht auszuführen, auszutauschen einer Zeit, zu.
Nachdem ich viele Artikel gelesen habe, bin ich mehr verwirrt als alles andere. Ich kann nicht genau bestimmen, was ich tun muss, um die drei "Jobs" parallel ausführen zu lassen, nicht einmal, ob dies der völlig falsche Weg ist oder ob es ein einfacher Konfigurationsfehler ist.
Ich bin Server und Client auf der gleichen Maschine laufen, mit netTcpBinding
die, wenn ich es richtig verstehe, benötigt wird und geeignet für mehrere parallele Anfragen zwischen Client und Server.
Ich habe gelesen und (hoffentlich) zu verstehen in dem folgenden Artikel, aber ich sehe nicht, wie dies zu meinem Fall übersetzt: tasks are still not threads and async is not parallel
Wie kann ich die Jobs machen, dass die Anrufe laufen auf verschiedenen Themen beantworten dann? Mir ist völlig bewusst, dass die Return Await
-Anweisung nur darauf wartet, dass die Ausführung beendet wird, aber ist dies nicht das Problem. Was ich will, ist drei dieser Aussagen warten parallel zu beenden aber die Pipeline zwischen dem Server und Client scheint nur eine Nachricht zu einer Zeit zu halten?
Vielen Dank für Ihre Zeit und Eingabe, ich schätze es wirklich.