Может кто-нибудь указать мне на хорошее рабочее решение следующей проблемы?
Приложение, над которым я работаю, должно взаимодействовать через TCP с программным обеспечением, работающим в другой системе.Некоторые запросы, которые я отправляю в эту систему, могут занять много времени (до 15 секунд).
В моем приложении есть несколько потоков, включая основной поток пользовательского интерфейса, который может обращаться к службе , которая взаимодействует с удаленной системой.Существует только один экземпляр службы, к которому обращаются все потоки.
Мне нужно разрешить обрабатывать только один запрос за один раз, то есть он должен быть сериализован, иначе это плохослучается с 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.
Я на самом деле использую устаревшую DLL-библиотеку VB6 для связи с лазером, поскольку в ней есть весь код для форматирования команд и обработки ответов.Эта библиотека VB6 использует элемент управления WinSock для связи.