.Net PropertyGrid Потокобезопасность - PullRequest
1 голос
/ 01 апреля 2009

У меня в приложении PropertyGrid, который используется для редактирования произвольных объектов. Мне нужно иметь возможность запускать произвольную подпрограмму в другом потоке, который также просматривает эти объекты (функциональность поиска, если вам интересно). Очевидная проблема заключается в том, что пользователь может редактировать один из этих объектов одновременно с тем, что моя поисковая цепочка читает его, что было бы предпочтительнее избегать (хотя это, вероятно, не приведет к чему-либо критическому, так как моя поисковая цепочка просто читает, не пишу).

Вызов lock(obj) достаточно прост из моей поисковой цепочки, но после просмотра документации и краткого просмотра кода PropertyDescriptorGridEntry в Reflector я не могу найти аналогичное место для использования вызова System.Threading.Monitor.Enter()/Exit() для объекта в вопросе о PropertyGrid. Я надеялся, что будут события BeginEdit и EndEdit, которые сделают это достаточно простым, но я не могу найти ничего подобного. Я бы предпочел не блокировать весь объект, пока он отображается в PropertyGrid, поскольку это, очевидно, блокировало бы мою поисковую цепочку, пока не был выбран другой объект.

Я немного новичок в многопоточной модели Windows Forms, так что я надеюсь, что есть какой-то очевидный ответ, который я только что пропустил. Любая помощь?

Редактировать: Синхронное клонирование моих объектов перед асинхронным поиском, вероятно, будет недостаточно эффективным, чтобы я мог также запустить сам поиск синхронно - точка асинхронного запуска, конечно, позволяет моим пользователям продолжать работать во время выполнения поиска. Поиск должен хорошо масштабироваться, так как набор данных, который я просматриваю, в конечном итоге окажется произвольно большим, что делает синхронное клонирование похожим на то, что это вызовет проблему юзабилити, которую я пытаюсь избежать.

Ответы [ 4 ]

0 голосов
/ 02 апреля 2009

Я могу ошибаться, но мне кажется, что вы идете не с того конца.

Есть две разные проблемы, которые вам нужно решить.

Во-первых, убедитесь, что PropertyGrid доступен только в потоке пользовательского интерфейса. Если какой-либо из его методов (включая свойство getter / setters) доступен из других потоков, вы будете страдать загадочным образом. Исключение составляют, конечно, InvokeRequired() и Invoke.

Во-вторых, убедитесь, что поисковая цепочка может работать правильно.

Чтобы решить первую проблему, либо убедитесь, что ваши объекты никогда не изменяются , за исключением потоком пользовательского интерфейса, или делают все ваши триггеры событий осведомленными о потоке так что события ваших объектов (например, PropertyChanged) запускаются только в потоке пользовательского интерфейса.

Вторая проблема проще - если ваша поисковая цепочка ЧИТАЕТ только ваши основные объекты, все должно работать нормально. Да, ваша поисковая цепочка может случайно увидеть некоторые частично обновленные данные, но действительно ли это проблема?

Пара заключительных мыслей ...

  • Не реализуйте свой поиск для перебора самой PropertyGrid; получить список объектов заранее (они не должны быть клонами) и проработать это вместо этого.

  • Рассматривали ли вы использование Idle Processing для поиска? Объект Application запускает событие каждый раз, когда в приложении заканчиваются сообщения для обработки - вы можете подключиться к нему и выполнять 1 шаг поиска при каждом запуске события. Что-то вроде потока бедняков, но без головной боли mutex / lock / semaphone. Я использовал это для очень хорошего эффекта в прошлом.

Обновление : Если я правильно помню, PropertyGrid учитывает интерфейс IEditableObject, вызывая BeginEdit, как только вы начнете изменять строку, и EndEdit при переходе в другой ряд. Вы можете использовать это, чтобы в поисковой цепочке не отображались неполные изменения.

Обновление 2 : В результате я обнаружил, что вы уже знали - PropertyGrid не не поддерживает интерфейс IEditableObject.

У меня есть еще одно предложение - хотя это может быть больше работы, чем вы хотите инвестировать.

Примерно в то время, когда в .NET 2.0 было представлено пространство имен System.Transactions, я увидел статью об использовании внешней транзакции для обеспечения изоляции потоков для объектов. Идея состоит в том, что свойства вашего объекта имеют двойное хранилище. Сначала у вас есть зафиксированное значение, видимое для всех потоков, и у вас есть локальная переменная потока, используемая для хранения незафиксированных значений для каждого потока. Когда свойство модифицируется, объект включается в любую внешнюю транзакцию, сохраняя новое значение в локальном потоке. Когда транзакция фиксируется или откатывается, значение для потока либо сохраняется, либо отбрасывается.

К сожалению, я не могу найти оригинальную статью, хотя кажется, что CSLA предоставляет эту поддержку. Надеюсь, это поможет.

0 голосов
/ 01 апреля 2009

Можете ли вы клонировать данные перед их отображением и заставить свою поисковую цепочку работать с клоном? Если вы хотите, чтобы цепочка поиска «видела» изменения, вы могли бы реагировать на события на PropertyGrid и, возможно, каким-то образом вносить контролируемые изменения в клон. (Вероятно, проще использовать «устаревшие» данные.)

Я знаю, что клонирование данных звучит ужасно неэффективно, но потоки, конечно, проще, когда каждый поток работает с полностью независимыми данными (или все потоки только читают).

0 голосов
/ 01 апреля 2009

Я думаю, вам придется проделать немалую работу для этого.

Во-первых, вам нужно будет создать реализацию ICustomTypeDescriptor, которая будет основывать его реализацию на любом TypeDescriptor, который вы обычно получаете для этого типа.

Затем в реализациях методов, которые предоставляют дескрипторы элементов, которые вы хотите заблокировать, вы предоставляете подклассы, которые наследуются от этих дескрипторов, и переопределяете соответствующие методы, чтобы обернуть блокировку.

Таким образом, для свойства вы должны реализовать GetProperties для возврата ваших определенных подклассов PropertyDescriptor. Эти подклассы переопределяют методы GetValue и SetValue (и другие) и используют блокировку при доступе к этим переменным.

Следует отметить, что подобная блокировка в потоке пользовательского интерфейса, вероятно, является плохой идеей, поскольку вы не хотите произвольно блокировать операции в этом потоке. Возможно, было бы лучше просто создать клон объекта и затем иметь метод, который обновляет хранилище объектов, когда вы закончите.

0 голосов
/ 01 апреля 2009

Это действительно не будет потокобезопасным. Вы могли бы разместить биты кода поиска в потоке пользовательского интерфейса, но это замедлит процесс и, возможно, победит суть потока ...

Насколько быстрым должен быть поиск? Вы можете работать против клона? и т.д.

...