Прочитать аргумент события после ожидания

У меня есть аппаратный компонент, который я использую для связи по протоколу TCP. Класс, управляющий им, имеет 3 задания: запуск устройства, прослушивание входящих сообщений и поднятие event когда сообщение получено:

public class Hardware
{
    public event Action<string> OnHardwareMessage;
    private NetworkStream stream = new NetworkStream();
    public Hardware()
    {
        Task.Factory.StartNew(() => { Listen(); });
    }

    private void Listen()
    {
        //listen to TCP port and raise an event when a message is received
        byte[] bytes = new byte[1024];
        int bytesRead = stream.Read(bytes, 0, bytes.Length);

        string response = Encoding.ASCII.GetString(bytes, 0, bytesRead);

        if (OnHardwareMessage != null)
            OnHardwareMessage(response);
    }

    public void Trigger()
    {
        //Trigger the hardware component
        //the response usually takes up to 5 seconds to arrive
    }
}

Этот класс используется в цикле внутри view-model:

public class MainViewModel
{
    private static EventWaitHandle hardwareWaiter =
        new EventWaitHandle(false, EventResetMode.AutoReset);

    private Hardware hardware = new Hardware();

    //what i'm doing now is holding a field for incoming event results
    private string hardwareResult;

    public MainViewModel()
    {
        hardware.OnHardwareMessage += hardware_OnHardwareMessage;

        while (true)
        {
            hardware.Trigger();
            if (hardwareWaiter.WaitOne(10000))
            {
                //is it possible to read the event argument here?

                //do something with the event argument
                someObservableCollection.Add(hardwareResult);

                //clear the value
                hardwareResult = string.Empty;
            }
            else
            {
                throw new Exception("Hardware did not respond on time");
            }
        }
    }

    //event listener for hardware events
    private void hardware_OnHardwareMessage(string message)
    {
        hardwareResult = message;
        hardwareWaiter.Set();
    }
}

Я запускаю устройство и жду ответа до 10 секунд. То, что я сейчас делаю, - это удержание поля области действия класса, назначение сообщения, полученного внутри прослушивателя событий, чтение его внутри цикла и очистка его.
У меня вопрос, есть ли какой-нибудь встроенный механизм, который мог бы позволить мне прочитать аргумент события сразу после EventWaitHandle было сообщено (за пределами слушателя события).

1 ответ

Я бы не стал основывать это на событиях. Может быть, ваш слушатель должен выставить BlockingCollection это представляет входящий поток сообщений. Затем читатели могут извлечь из этой коллекции тайм-аут. Если время ожидания истекло, сообщение не принимается и не теряется (это условие гонки, которое у вас есть сейчас).

Если вы настаиваете на использовании событий, используйте замыкание для передачи состояния сообщения:

string hardwareResult = null;
Action handler = (...) =>
    {
        hardwareResult = message;
        hardwareWaiter.Set();
    };

    hardware.OnHardwareMessage += handler;

Таким образом, вам нужна только локальная переменная, которая имеет более узкую область, чем поле.

Также есть обычная ошибка TCP: вы предполагаете прочитать сообщение целиком. Вместо этого вы можете читать по одному байту за раз.

Другие вопросы по тегам