Проблемы параллелизма при доступе к данным через отражение в C # - PullRequest
1 голос
/ 12 октября 2009

В настоящее время я пишу библиотеку, которую можно использовать для отображения внутреннего состояния некоторого работающего кода (в основном полей и свойств, как открытых, так и закрытых). К объектам обращаются в другом потоке, чтобы поместить их информацию в окно, чтобы пользователь мог их видеть. Проблема в том, что иногда я иду по длинному IList, в котором его структура может измениться. Некоторый фрагмент кода в наблюдаемой программе может добавить новый элемент или, что еще хуже, удалить его. Это, конечно, приводит к краху.

Я выдвинул некоторые идеи, но, боюсь, они не совсем верны:

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

  2. Пусть просматриваемый код знает о моем существовании и предоставляет некоторые интерфейсы для синхронизации. (Хотя мне бы очень хотелось, чтобы он был полностью прозрачным).

  3. В крайнем случае, поместите каждый доступ на чтение в блок try / catch и сделайте вид, будто ничего не произошло, когда он выбрасывает. (Не могу придумать более уродливое решение, которое действительно работает).

Заранее спасибо.

Ответы [ 5 ]

4 голосов
/ 12 октября 2009

Единственный способ сделать вещи "прозрачными" для отслеживаемого кода - это сделать код мониторинга устойчивым к изменениям состояния.

Некоторые предложения

  1. Не просматривайте общий список - сделайте копию списка в локальный экземпляр List как можно быстрее (и так быстро), как только можете. Если у вас есть локальный (не общий) список экземпляров, никто не может связываться со списком.

  2. Сделайте вещи настолько надежными, насколько это возможно - помещать каждое чтение в попытку / уловку может показаться неприятным, но вам, вероятно, придется это сделать.

1 голос
/ 12 октября 2009

Как я понимаю, вы не хотите иметь ЛЮБОЙ зависимости / требования к наблюдаемому коду или применять какие-либо ограничения на то, как код написан.

Хотя это мой любимый подход к кодированию «наблюдателя», это приводит к тому, что ваше приложение сталкивается с очень широким диапазоном кода и поведения, что может привести к его аварийному завершению.

Итак, как я уже говорил, я советую сделать наблюдателя «надежным» на первом этапе. Вы должны быть готовы к тому, что в вашем коде что-то пойдет не так, потому что, учитывая «прозрачность», многие вещи могут пойти не так! (Будьте осторожны, куда поместить ваш try / catch, вход и выход из блока try много раз может оказать заметное влияние на производительность)

Когда вы закончите делать свой код устойчивым, следующие шаги будут сделать его более удобным и уклоняться от ситуаций, которые могут вызвать исключения, такие как «список», который вы упомянули. Например, вы можете проверить наблюдаемый объект и посмотреть, является ли он списком, и он не слишком длинный, сначала сделайте его быструю копию, а затем сделайте все остальное. Таким образом вы устраняете большую вероятность того, что ваш код может сработать.

1 голос
/ 12 октября 2009

Вариант № 3 может показаться уродливым, но это похоже на подход, используемый в окнах наблюдения Visual Studio, и я бы выбрал этот подход.

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

Это наиболее надежный подход при работе с таким открытым диапазоном возможностей. Дело в том, что если ваш код просмотра предназначен для поддержки как можно большего количества сценариев, вы не сможете заранее продумать все ситуации. Обработка и представление исключений - следующий лучший подход.

Кстати, кто-то еще упомянул, что блокировка ваших структур данных будет работать. Это неверно, если «другой код» также не использует блокировки для синхронизации. Фактически оба фрагмента кода должны блокировать один и тот же объект синхронизации, что очень маловероятно, если вы не управляете другим кодом. (Я думаю, что вы упомянули об этом в своем вопросе, поэтому я согласен.)

1 голос
/ 12 октября 2009

Хотя мне нравится идея Бевана о копировании списка для локального доступа для чтения, если этот список особенно велик, это не может быть действительно жизнеспособным вариантом.

Если вам действительно нужен беспроблемный, прозрачный, одновременный доступ к этим спискам, вам следует обратиться к библиотеке Parallel Extensions for .NET. В настоящее время он доступен для .NET 2.0 через 3.5 как CTP Расширения будут официально включены в .NET 4.0 вместе с некоторыми дополнительными коллекциями. Я думаю, что вас заинтересует BlockingCollection от CTP, который предоставит вам тот прозрачный параллельный доступ, который вам необходим. Очевидно, что производительность падает, как и в случае с любыми многопоточными средствами, которые включают синхронизацию, однако эти коллекции довольно хорошо оптимизированы.

0 голосов
/ 12 октября 2009

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

Тем не менее, мне кажется, что я бы избегал блокировки (потому что кажется, что ваш поток является только «наблюдателем» и не должен прерывать).

Исходя из этого, я бы просто попытался разобраться со случаями, когда вы определяете пропущенные вещи. Разве это не возможно?

...