如何序列化对异步资源的访问?

can*_*ice 1 c# multithreading tcp

谁能为我指出以下问题的有效解决方案?

我正在处理的应用程序需要通过TCP与另一系统上运行的软件进行通信。我发送到该系统的某些请求可能需要很长时间才能完成(最多15秒)。

在我的应用程序中,我有很多线程,包括主UI线程,可以访问与远程系统通信的服务。所有线程都只能访问该服务的一个实例。

我只需要一次处理一个请求,即它需要序列化,否则TCP通讯会发生不好的事情。

到目前为止尚未尝试的解决方案

最初,我尝试将lock()与静态对象一起使用以保护每个“命令”方法,如下所示:

lock (_cmdLock)
{
SetPosition(position);
}
Run Code Online (Sandbox Code Playgroud)

但是我发现,即使远程系统和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;
        }

    }
Run Code Online (Sandbox Code Playgroud)

我没有尝试实现命令请求队列,因为调用代码希望所有命令都是同步的,即,上一条命令必须在我发送下一条命令之前完成。

其他背景

在远程系统上运行的软件是第三方产品,我无法访问它,它用于控制带有集成XY工作台的激光打标机。

我实际上正在使用旧版VB6 DLL与激光器进行通信,因为它具有格式化命令和处理响应的所有代码。此VB6 DLL使用WinSock控件进行通讯。

Bri*_*new 5

我不确定为什么排队解决方案不起作用。

为什么不将每个请求以及带有结果的回调的详细信息放在队列中呢?您的应用程序会将这些请求放入队列,并且与您的第三方系统接口的模块可以依次获取每个队列项,进行处理并返回结果。

我认为这是模块之间关注的更清晰的分离,而不是在请求分发等周围实现锁定。您的请求者基本上忽略了序列化约束,并且第三方接口模块可以照顾序列化,管理超时和其他错误等。

编辑:在Java世界中,我们有BlockingQueues,它可以为使用者/发布者同步,并使这种事情变得很容易。我不确定在C#世界中是否也有相同的代码。快速搜索没有提示,但是这种事情有源代码(如果C#世界中的任何人都可以理解的话)