Страницы

Поиск по вопросам

вторник, 17 декабря 2019 г.

Как организовать синхронизацию потоков?

#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; } } }

Комментариев нет:

Отправить комментарий