0

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.

Antwort

Verwandte Themen