有人能指出我对以下问题的良好解决方案吗?
我正在处理的应用程序需要通过TCP与另一个系统上运行的软件进行通信。我发送到该系统的一些请求可能需要很长时间才能完成(最多15秒)。
在我的应用程序中,我有许多线程,包括主UI线程,可以访问与远程系统通信的 服务 。所有线程都只有一个服务实例。
我只需要允许一次处理一个请求,即需要序列化,否则TCP通信会发生坏事。
到目前为止尝试的解决方案
最初我尝试使用带有静态对象的lock()来保护每个'command'方法,如下所示:
lock (_cmdLock)
{
SetPosition(position);
}
但是我发现有时它不会释放锁,即使远程系统和TCP通信上有超时。另外,如果两个调用来自同一个线程(例如,用户双击一个按钮),那么它将通过锁定 - 在再次读取锁定之后我知道同一个线程不会等待锁定。
然后我尝试使用AutoResetEvents一次只允许一次调用。但是如果没有锁定,它将不适用于多个线程。以下是我用来发送命令的代码(来自调用线程)并处理命令请求(在后台运行自己的线程)
private static AutoResetEvent _cmdProcessorReadyEvent = new AutoResetEvent(false);
private static AutoResetEvent _resultAvailableEvent = new AutoResetEvent(false);
private static AutoResetEvent _sendCommandEvent = new AutoResetEvent(false);
// This method is called to send each command and can run on different threads
private bool SendCommand(Command cmd)
{
// Wait for processor thread to become ready for next cmd
if (_cmdProcessorReadyEvent.WaitOne(_timeoutSec + 500))
{
lock (_sendCmdLock)
{
_currentCommand = cmd;
}
// Tell the processor thread that there is a command present
_sendCommandEvent.Set();
// Wait for a result from the processor thread
if (!_resultAvailableEvent.WaitOne(_timeoutSec + 500))
_lastCommandResult.Timeout = true;
}
return _lastCommandResult.Success;
}
// This method runs in a background thread while the app is running
private void ProcessCommand()
{
try
{
do
{
// Indicate that we are ready to process another commnad
_cmdProcessorReadyEvent.Set();
_sendCommandEvent.WaitOne();
lock (_sendCmdLock)
{
_lastCommandResult = new BaseResponse(false, false, "No Command");
RunCOMCommand(_currentCommand);
}
_resultAvailableEvent.Set();
} while (_processCommands);
}
catch (Exception ex)
{
_lastCommandResult.Success = false;
_lastCommandResult.Timeout = false;
_lastCommandResult.LastError = ex.Message;
}
}
我没有尝试实现命令请求队列,因为调用代码期望所有内容都是同步的 - 即在发送下一个命令之前必须完成上一个命令。
其他背景
远程系统上运行的软件是第三方产品,我无法访问它,它用于控制带有集成XY工作台的激光打标机。
我实际上使用传统的VB6 DLL与激光进行通信,因为它具有格式化命令和处理响应的所有代码。此VB6 DLL使用WinSock控件进行通信。
答案 0 :(得分:4)
我不确定为什么排队解决方案不起作用。
为什么不将每个请求以及带有结果的回调的详细信息放在队列中?您的应用程序将对这些请求进行排队,与第三方系统连接的模块可以依次获取每个队列项 ,处理并返回结果。
我认为这是模块间关注点的清晰分离,而不是围绕请求调度等实现锁定。您的请求者基本上不了解序列化约束,第三方接口模块可以处理序列化,管理超时和其他错误等
编辑:在Java世界中,我们有BlockingQueues,它们为消费者/发布者同步,使这类事情变得非常简单。我不确定你是否在C#世界中拥有相同的东西。一个快速搜索建议不要,但是有这样的东西浮动的源代码(如果C#世界中的任何人可以放弃一些值得赞赏的光)