Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top