按顺序执行对异步 WCF 服务的调用
Calls to async WCF Service executed sequentially
我构建了一个基本的 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
一个 "Request" 对象包含一个 "Command" 对象(派生自 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.
要使用数据和请求响应,我有以下 class:
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
)class 用于将整数"n" 传输到服务器。然后,服务器使用 HelloPackage
(派生自 PackageBase
)响应以下 n GetNextPackage
次调用,最后响应 EndPackage
(派生自 PackageBase
) .
此逻辑在 ServiceJob 对象(派生自 ServiceJobBase
)中处理 - 基本上每个 "Command" 对象都有一个相应的 "ServiceJob" 对象,该对象又发送相应的 "Package" 反对顺序客户端请求。
由于客户端处理 "data requests" 所需的 "sequentiality",即对 GetNextPackage
函数的顺序调用,这些调用永远不会重叠。但我非常希望在服务器上并行执行对 GetNextPackage
及其各自的 "ServiceJobs" 的两个或多个单独调用序列。而这并没有发生。
向 HelloServiceJob
class 添加一个简单的计数器以轻松识别每个请求,在我的 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 秒,客户端和服务器一次只交换消息。
看了很多文章,比什么都糊涂了。我无法确定我需要做什么才能使三个 "Jobs" 并行执行,甚至不知道这是完全错误的方法还是简单的配置错误。
我运行在同一台机器上连接服务器和客户端,使用netTcpBinding
,如果我理解正确的话,它是必需的,适用于客户端和服务器之间的多个并行请求。
我已阅读并(希望)理解以下文章,但我不明白这如何转化为我的情况:tasks are still not threads and async is not parallel
我怎样才能在不同的 线程 上创建正在接听电话 运行 的作业?我完全知道 Return Await
语句只是等待执行完成,但 这不是问题所在 。我想要的是其中三个语句 等待并行完成 但是服务器和客户端之间的管道似乎一次只保存一条消息?
谢谢大家的时间和投入,我真的很感激。
设置ConcurrencyMode:=ConcurrencyMode.Multiple
解决问题。
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
Public Class GetDataService
[...]
End Class
ServiceBehaviorAttribute.ConcurrencyMode Property
默认为"Single"
我构建了一个基本的 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
一个 "Request" 对象包含一个 "Command" 对象(派生自 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.
要使用数据和请求响应,我有以下 class:
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
)class 用于将整数"n" 传输到服务器。然后,服务器使用 HelloPackage
(派生自 PackageBase
)响应以下 n GetNextPackage
次调用,最后响应 EndPackage
(派生自 PackageBase
) .
此逻辑在 ServiceJob 对象(派生自 ServiceJobBase
)中处理 - 基本上每个 "Command" 对象都有一个相应的 "ServiceJob" 对象,该对象又发送相应的 "Package" 反对顺序客户端请求。
由于客户端处理 "data requests" 所需的 "sequentiality",即对 GetNextPackage
函数的顺序调用,这些调用永远不会重叠。但我非常希望在服务器上并行执行对 GetNextPackage
及其各自的 "ServiceJobs" 的两个或多个单独调用序列。而这并没有发生。
向 HelloServiceJob
class 添加一个简单的计数器以轻松识别每个请求,在我的 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 秒,客户端和服务器一次只交换消息。
看了很多文章,比什么都糊涂了。我无法确定我需要做什么才能使三个 "Jobs" 并行执行,甚至不知道这是完全错误的方法还是简单的配置错误。
我运行在同一台机器上连接服务器和客户端,使用netTcpBinding
,如果我理解正确的话,它是必需的,适用于客户端和服务器之间的多个并行请求。
我已阅读并(希望)理解以下文章,但我不明白这如何转化为我的情况:tasks are still not threads and async is not parallel
我怎样才能在不同的 线程 上创建正在接听电话 运行 的作业?我完全知道 Return Await
语句只是等待执行完成,但 这不是问题所在 。我想要的是其中三个语句 等待并行完成 但是服务器和客户端之间的管道似乎一次只保存一条消息?
谢谢大家的时间和投入,我真的很感激。
设置ConcurrencyMode:=ConcurrencyMode.Multiple
解决问题。
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Multiple)>
Public Class GetDataService
[...]
End Class
ServiceBehaviorAttribute.ConcurrencyMode Property
默认为"Single"