#c# #lambda #delegates #compact-framework
#c# #лямбда #делегаты #compact-framework
Вопрос:
Я ищу рекомендации по архитектуре, а также более глубокое понимание делегатов и лямбд (в дополнение к необходимости исправить реальную проблему!)
У нас есть код, взаимодействующий с устройством (масштабом) через последовательный порт на кпк. Мы подключаем представление для получения данных с устройства. Поскольку к нашему экземпляру scale одновременно «подключается» только одно представление, мы использовали свойство типа Action для обработки взаимодействия между экземпляром scale и представлением (вместо подписки на событие). Затем представление присваивает этому свойству значение lambda, которое принимает значение из шкалы и изменяет пользовательский интерфейс.
Проблема, с которой мы в настоящее время сталкиваемся, заключается в удалении нашего представления. Если масштаб в данный момент отправляет данные (и мы находимся внутри обработчика действий), когда представление закрывается пользователем (в это время мы принудительно удаляем, поскольку мы используем CF), приложение зависает: лямбда-выражение действия никогда не завершается, И удаление экземпляра scale зависает при попытке закрыть SerialPort.
-
Есть ли ключевое различие в том, как обрабатывается Action — свойство класса в подобной ситуации по сравнению с событием?
-
Основываясь на деталях журнала, код находится внутри лямбда-выражения Action (которое изменяет некоторые элементы пользовательского интерфейса) при вызове Dispose в представлении. Оба они находятся в потоке пользовательского интерфейса — как они оба могут выполняться одновременно? Я что, недостаточно выспался прошлой ночью?
-
Кто-нибудь видит здесь какие-то плохие архитектурные решения, которые следует исправить?
Спасибо. Я могу попытаться получить несколько примеров кода здесь, если описание не имеет достаточного смысла.
Комментарии:
1. 2 звучит как возможно повторный вход?
Ответ №1:
Когда не используется в качестве дерева выражений, лямбда-выражение преобразуется в обычный делегат, так что это не должно быть проблемой.
Но все это звучит как проблема взаимоблокировки / параллелизма. Вместо того, чтобы закрывать последовательный порт напрямую, используйте сигнал на время действия обработчика действия (который, вероятно, одновременно выполняется в другом потоке — проверьте это еще раз), чтобы вы могли корректно дождаться его завершения перед закрытием порта.
Комментарии:
1. Как отмечено в комментариях Рида Копси к его ответу, событие DataReceived вызывается во вторичном потоке. Мы добавили сигнал, как предложил Lucero, и проблема, кажется , решена.
2. Стив, спасибо за продолжение. Рад, что предложение сработало для вас.
Ответ №2:
-
Нет — событие на самом деле просто делегат, как и действие
-
Они не могут выполняться в одно и то же время в одном потоке — более вероятно, что вы размещаете в отдельном потоке, или обработчик действий выполняется в отдельном потоке. Если это происходит в ответ на последовательный порт, поймите, что события последовательного порта происходят в фоновых потоках.
-
Здесь действительно недостаточно информации, чтобы сказать, что следует или не следует изменять с архитектурной точки зрения. При этом я задаюсь вопросом, почему вы не используете события — на самом деле нет никакого преимущества в использовании «действия» по сравнению с использованием обработчика событий, но последнее звучит так, как будто оно более четко соответствует ментальной модели и более четко покажет ваше намерение.
Комментарии:
1. 2. Я думал, что события порта происходят в фоновом потоке, но мне не нужно вызывать Invoke для обновления пользовательского интерфейса, поэтому я подумал, что, должно быть, ошибаюсь. Теперь я в замешательстве относительно того, почему мне не нужен Invoke. 3. Лень и не видение выгоды от события, когда действие<T> настолько простое в использовании.
2. @Steve: Вы упомянули, что находитесь на CF — в зависимости от платформы обновления пользовательского интерфейса из потока bg не всегда вызывают исключения, особенно на CF. Однако они по-прежнему не поддерживаются операцией, поэтому вам все равно следует использовать invoke . Что касается потоковой передачи — смотрите SerialPort.DataReceived: msdn.microsoft.com/en-us/library/… «Событие DataReceived вызывается во вторичном потоке при получении данных от объекта SerialPort»