Потокобезопасный дизайн библиотеки классов - PullRequest
2 голосов
/ 10 августа 2009

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

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

В настоящее время у меня есть подход, если у меня есть класс A, тогда я создаю несколько экземпляров InternalA для каждого экземпляра A. InternalA обладает многими важными свойствами из класса A, но является внутренним недоступным вне библиотеки.

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

Похоже ли это на правильный подход?

Ответы [ 4 ]

2 голосов
/ 10 августа 2009

Почему бы и нет? Создайте универсальный класс, который принимает класс из двух членов (например, Lock / Unlock), чтобы вы могли предоставить

  • Threadsafe Impl (Implmenetation может использовать Monitor.Enter / Exit внутри)
  • Безопасный для всей системы impl (с использованием Mutex)
  • Небезопасно, но быстро (с использованием пустого значения).
2 голосов
/ 10 августа 2009

Трудно сказать по-настоящему, но я могу сказать, что если вы сможете сделать все неизменным, ваша жизнь станет намного проще. Посмотрите, как функциональные языки подходят к неизменным структурам данных и коллекциям. Чем меньше у вас общих изменяемых данных, тем проще будет создать поток.

1 голос
/ 10 августа 2009

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

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

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

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

выполняя это, вы получаете двойной буфер ... (поток 1 создает отчет об изменениях, в то время как поток 2 использует последний отчет). Если вы добавляете больше интерфейсов (и потоков), создается впечатление, что в потоках движутся импульсы работы.

Это было основано на моих исследованиях, и я не сомневаюсь, что сейчас есть лучшие методы.

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

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

0 голосов
/ 12 августа 2009

Немного больше информации по проблеме, чтобы помочь ...

Эвристика, которую я использую, заключается в решении проблем, подобных TSP. Что происходит в начале каждого Расчет состоит в том, что все аспекты, которые формируют проблему (продавец / места для посещения), клонируются поэтому они не затронуты в потоках.

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

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

Например

class A
{
   public double Stock { get; }

   // Processing and cloning actually works using these InternalA's
   internal InternalA ConvertToInternal() {}
}

internal class InternalA : ICloneable
{
   public double Stock { get; set; }

   public bool HasSufficientStock() {}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...