对异步 WCF 服务的调用按顺序执行

问题描述 投票:0回答:1

我已经构建了一个基本的 WCF 控制台服务器应用程序。我的目标是以并行方式处理对其的多个调用,但当前版本按顺序处理它们。

请耐心等待,因为有一堵代码需要遵循,但这是非常基本的东西。对我来说很难隔离,因为它是相当大的 VS 解决方案的一部分。

我已经走上了 TPL 和 async/await 关键字的道路,我基本上理解并喜欢它们。

服务接口:

<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

代理:

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

以及实施:

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

“请求”对象包含“命令”对象(源自

CommandBase
)以及附加信息。 “Package”对象(源自
PackageBase
)包含要从服务器传输到客户端的数据。

沟通的基本思路是这样的:

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.

为了使用数据和请求响应,我有以下类:

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

在我的 WPF 测试客户端应用程序中,我有一个按钮,我可以用它向服务器发送请求。

HelloCommand
(派生自
CommandBase
)类用于将整数“n”传输到服务器。然后,服务器使用
GetNextPackage
(源自
HelloPackage
)响应接下来的每个
PackageBase
调用,最后使用
EndPackage
(源自
PackageBase
)。

此逻辑在 ServiceJob 对象中处理(源自

ServiceJobBase
) - 基本上每个“Command”对象都有一个相应的“ServiceJob”对象,该对象又将相应的“Package”对象发送到顺序客户端请求。

当客户端处理“数据请求”所需的“顺序性”时,即对

GetNextPackage
函数的顺序调用,这些调用永远不会重叠。但我非常希望两个或多个单独的
GetNextPackage
调用序列 - 及其各自的“ServiceJobs” - 在服务器上并行执行。但这并没有发生。

HelloServiceJob
类添加一个简单的计数器以轻松识别每个请求,只需按一下 WPF 客户端中的按钮即可在服务器上生成以下输出,同时 UI 保持响应。

Request recieved (0)
Sending HelloPackage - 6 remaining (0)
Sending HelloPackage - 5 remaining (0)
Sending HelloPackage - 4 remaining (0)
Sending HelloPackage - 3 remaining (0)
Sending HelloPackage - 2 remaining (0)
Sending HelloPackage - 1 remaining (0)
Sending HelloPackage - 0 remaining (0)
Sending no more HelloPackages

每行之间有 1.5 秒的预期。

快速连续按三下会在服务器上产生以下输出,同时 UI 保持响应。

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)

虽然订单是预期的,但每行执行需要 1.5 秒,客户端和服务器一次仅交换消息。

看了很多文章,我比什么都困惑。我无法确定我需要做什么才能使三个“作业”并行执行,甚至无法确定这是否是完全错误的方法或者是否是一个简单的配置错误。

我在同一台机器上运行服务器和客户端,使用

netTcpBinding
,如果我理解正确的话,这是需要的,并且适合客户端和服务器之间的多个并行请求。

我已阅读并(希望)理解以下文章,但我不知道这如何转化为我的情况:任务仍然不是线程,异步不是并行

如何让正在应答呼叫的作业在不同的线程上运行?我完全知道

Return Await
语句只是等待执行完成,但是这不是问题。我想要的是其中三个语句等待并行完成,但服务器和客户端之间的管道似乎一次只保存一条消息?

感谢大家的时间和投入,我真的很感激。

vb.net multithreading wcf asynchronous task-parallel-library
1个回答
1
投票

设置

ConcurrencyMode:=ConcurrencyMode.Multiple
即可解决问题。

<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
Public Class GetDataService
    [...]
End Class

服务行为属性.并发模式属性

默认为“单张”

© www.soinside.com 2019 - 2024. All rights reserved.