C# 基于TPL数据流的网络命令处理
我正在开发一个系统,该系统涉及通过TCP网络连接接受命令,然后在执行这些命令时发送响应。相当基本的东西,但我希望支持一些要求:C# 基于TPL数据流的网络命令处理,c#,.net,asynchronous,tpl-dataflow,C#,.net,Asynchronous,Tpl Dataflow,我正在开发一个系统,该系统涉及通过TCP网络连接接受命令,然后在执行这些命令时发送响应。相当基本的东西,但我希望支持一些要求: 多个客户端可以同时连接并建立单独的会话。会话可以持续任意长或短,如果需要,同一个客户端IP可以建立多个并行会话 每个会话可以同时处理多个命令,因为某些请求的操作可以并行执行 我希望使用async/await干净地实现这一点,根据我所读到的内容,TPL数据流听起来是一种很好的方法,可以干净地将处理分解为可以在线程池上运行的良好块,而不是为不同的会话/命令捆绑线程,阻塞等待
private readonly任务\u serviceTask;
私有只读任务_commandsTask;
私有只读取消令牌源\u取消;
专用只读缓冲块_pending命令;
公用网络服务(ICommandProcessor commandProcessor)
{
_commandProcessor=commandProcessor;
IsRunning=true;
_取消=新的CancellationTokenSource();
_pendingCommands=新的缓冲块();
_serviceTask=Task.Run((Func)RunService);
_commandsTask=Task.Run((Func)RunCommands);
}
公共bool正在运行{get;private set;}
专用异步任务RunService()
{
_侦听器=新的TcpListener(IPAddress.Any,ServicePort);
_listener.Start();
同时(正在运行)
{
socketclient=null;
尝试
{
client=wait_listener.AcceptSocketAsync();
client.Blocking=false;
var session=RunSession(客户端);
锁定(_会话)
{
_添加(会话);
}
}
捕获(例外情况除外)
{//在这里处理。。。
}
}
}
专用异步任务RunCommands()
{
同时(正在运行)
{
var command=wait _pendingCommands.ReceiveAsync(_cancellation.Token);
var task=task.Run(()=>RunCommand(command));
}
}
专用异步任务RunCommand(Command)
{
尝试
{
var response=await_commandProcessor.RunCommand(command.Content);
发送(command.Client,response);
}
捕获(例外情况除外)
{
//在这里处理常规命令异常。。。
}
}
专用异步任务运行会话(套接字客户端)
{
while(client.Connected)
{
变量读取器=新的分隔命令读取器(客户端);
尝试
{
var content=await reader.ReceiveCommand();
_Post(新命令(客户端,内容));
}
捕获(例外情况除外)
{
//这里的异常处理。。。
}
}
}
基本原理看起来很简单,但有一部分让我大吃一惊:如何确保在关闭应用程序时,我等待所有挂起的命令任务完成?当我使用Task.Run来执行命令时,我得到了Task对象,但是如何跟踪挂起的命令,以便在允许服务关闭之前确保所有命令都已完成
我曾考虑过使用一个简单的列表,在命令完成后从列表中删除命令,但我想知道我是否缺少一些TPL数据流中的基本工具,这些工具可以让我更干净地完成这项工作
编辑:
在阅读更多关于TPL数据流的内容时,我想知道我应该使用的是一个增加了MaxDegreeOfParallelism以允许处理并行命令的方法吗?这为可以并行运行的命令数量设置了上限,但我认为这对我的系统来说是一个合理的限制。我很想从那些有TPL数据流经验的人那里了解我是否走上了正确的道路。任务默认为后台任务,这意味着当应用程序终止时,它们也会立即终止。您应该使用线程而不是任务。然后您可以设置:
Thread.IsBackground = false;
这将防止应用程序在工作线程运行时终止。
当然,这需要对上述代码进行一些更改
更重要的是,在执行shutdown方法时,您还可以等待来自主线程的任何未完成任务
我看不到更好的解决办法 是的,所以。。。你有点在使用第三方物流的力量。如果您订阅的是TPL数据流样式,那么在后台
任务中的while循环中仍然手动从BufferBlock
接收项目这一事实并不是您想要的“方式”
您要做的是将一个ActionBlock
链接到BufferBlock
,并在其中进行命令处理/发送。在这个块中,您可以设置MaxDegreeOfParallelism
,以控制要处理的并发命令的数量。因此,设置可能如下所示:
// Initialization logic to build up the TPL flow
_pendingCommands = new BufferBlock<Command>();
_commandProcessor = new ActionBlock<Command>(this.ProcessCommand);
_pendingCommands.LinkTo(_commandProcessor);
private Task ProcessCommand(Command command)
{
var response = await _commandProcessor.RunCommand(command.Content);
this.Send(command.Client, response);
}
如果你想获得额外积分,你甚至可以将命令处理和命令发送分开。这将允许您分别配置这些步骤。例如,您可能需要限制处理命令的线程数量,但希望有更多线程发送响应。您只需在流的中间引入一个TransformBlock
即可:
_pendingCommands = new BufferBlock<Command>();
_commandProcessor = new TransformBlock<Command, Tuple<Client, Response>>(this.ProcessCommand);
_commandSender = new ActionBlock<Tuple<Client, Response>(this.SendResponseToClient));
_pendingCommands.LinkTo(_commandProcessor);
_commandProcessor.LinkTo(_commandSender);
private Task ProcessCommand(Command command)
{
var response = await _commandProcessor.RunCommand(command.Content);
return Tuple.Create(command, response);
}
private Task SendResponseToClient(Tuple<Client, Response> clientAndResponse)
{
this.Send(clientAndResponse.Item1, clientAndResponse.Item2);
}
_pendingCommands=new B
_pendingCommands.Complete();
_commandProcessor.Completion.Wait();
_pendingCommands = new BufferBlock<Command>();
_commandProcessor = new TransformBlock<Command, Tuple<Client, Response>>(this.ProcessCommand);
_commandSender = new ActionBlock<Tuple<Client, Response>(this.SendResponseToClient));
_pendingCommands.LinkTo(_commandProcessor);
_commandProcessor.LinkTo(_commandSender);
private Task ProcessCommand(Command command)
{
var response = await _commandProcessor.RunCommand(command.Content);
return Tuple.Create(command, response);
}
private Task SendResponseToClient(Tuple<Client, Response> clientAndResponse)
{
this.Send(clientAndResponse.Item1, clientAndResponse.Item2);
}