#c_sharp #net #многопоточность
Есть поток, который в цикле ожидает данные от ком-порта (или любого другого интерфейса) и при их поступлении вызывает событие OnFrameReceived(...). Мне нужно написать блокирующий метод SendAndWaitReply(...), который отправит данные по ком-порту и будет ждать ответа (события OnFrameReceived) или таймаута операции (если ответ не придет). Подскажите, как организовать синхронизацию потока чтения и потока, отправляющего команду? public class Device { Connection connection = new Connection(...); public Device() { connection.FrameReceived += Connection_FrameReceived; } // Отправить пакет и дождаться ответа/таймаута public void SendAndWaitReply(ITxFrame frame) { bool timeout = false; // Отправить пакет и заблокировать поток до наступления таймаута или приема ответа if(timeout) { throw new TimeoutException(); } } // Событие приема пакета private void Connection_FrameReceived(object sender, RxFrame frame) { // Разблокировать поток, ожидающий приема пакета // Обработка принятого пакета } }
Ответы
Ответ 1
Не знаю, можно ли проще: public class Device { private object _sendAndWaitReplySyncObject = new object(); private AutoResetEvent _frameReceivedEvent = new AutoResetEvent(false); private AutoResetEvent _frameAcceptedEvent = new AutoResetEvent(false); private AutoResetEvent _timeoutEvent = new AutoResetEvent(false); private ManualResetEvent _outsideSendAndWaitReplyEvent = new ManualResetEvent(true); // Отправить пакет и дождаться ответа/таймаута public void SendAndWaitReply(object frame) { try { lock (_sendAndWaitReplySyncObject) { // переводим все эвенты в not signaled состояние _outsideSendAndWaitReplyEvent.Reset(); _frameReceivedEvent.Reset(); _frameAcceptedEvent.Reset(); _timeoutEvent.Reset(); // Отправить пакет bool timeout = !_frameReceivedEvent.WaitOne(3000); if (timeout) { _timeoutEvent.Set(); // для того, что бы блок finally не сработал раньше времени _frameReceivedEvent.WaitOne(); throw new TimeoutException(); } else { _frameAcceptedEvent.Set(); // для того, что бы блок finally не сработал раньше времени _frameReceivedEvent.WaitOne(); } } } finally { // что бы Connection_FrameReceived не ждал вечно на вызове WaitAll _outsideSendAndWaitReplyEvent.Set(); } } // Событие приема пакета private void Connection_FrameReceived(object sender, object frame) { _frameReceivedEvent.Set(); // Разблокировать поток, ожидающий приема пакета int index = WaitHandle.WaitAny(new WaitHandle[] { _outsideSendAndWaitReplyEvent, _frameAcceptedEvent, _timeoutEvent }); switch (index) { case 0: // событие сработало вне метода SendAndWaitReply break; case 1: _frameReceivedEvent.Set(); // Обработка принятого пакета break; case 2: _frameReceivedEvent.Set(); // сработал _timeoutEvent break; } } }
Комментариев нет:
Отправить комментарий