C ++ / Boost: синхронизировать доступ к ресурсу через вызовы нескольких методов (получателей) - PullRequest
2 голосов
/ 03 июля 2011

Я не уверен, если это вопрос касательно техники или дизайна, но я открыт для предложений.

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

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

class IGpsSensor {

    public:
        virtual float getLongitude() = 0;
        virtual float getLatitude() = 0;
        virtual float getElevation() = 0;

        // Deviations
        virtual float getLongitudeDev() = 0;
        virtual float getLatitudeDev() = 0;
        virtual float getElevationDev() = 0;

        virtual int getNumOfSatellites() = 0;
};

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

Пока все хорошо.В большинстве случаев этого уровня синхронизации должно быть достаточно.Однако иногда может потребоваться получить более одного значения (с последовательными вызовами getXXX ()) и убедиться, что между ними не происходит никаких обновлений.Нужно это или нет (и какие ценности важны), зависит от потребителя.

Придерживаясь примера, во многих случаях важно знать только долготу и широту (но, надеюсь, оба имеют отношение к одному и тому же обновлению ()).Я допускаю, что это можно сделать, сгруппировав их в класс или структуру "Position".Но потребитель может также использовать датчик для более сложного алгоритма и также требует отклонения.

Теперь мне было интересно, что было бы правильным способом сделать это.

Решения, о которых я мог подумать:

  • Сгруппировать все возможные значения в структуру (или класс) и добавить дополнительный (синхронизированный) метод получения, возвращающий копии всех значений одновременно- мне кажется, что это лишние накладные расходы, если требуется только 2 или 3 значения из 10.

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

Спасибо за вашу помощь.

Ответы [ 3 ]

2 голосов
/ 03 июля 2011

Как насчет предоставления интерфейса "Reader"?Чтобы получить объект для чтения, вы должны сделать что-то вроде этого:

const IGpsSensorReader& gps_reader = gps_sensor.getReader();

Класс IGpsSensorReader может иметь доступ к защищенным членам класса IGpsSensor.Когда построено, это приобретет замок.После уничтожения это освободит замок.Аксессор может сделать что-то вроде этого:

{ //block that accesses attributes
   const IGpsSensorReader& gps_reader = gps_sensor.getReader();
   //read whatever values from gps_reader it needs
} //closing the scope will destruct gps_reader, causing an unlock

Вы также можете представить метод getWriter потоку, выполняющему обновления.Внутри вы можете использовать boost shared_mutex для обеспечения доступа между читателями и писателями.

2 голосов
/ 03 июля 2011

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

1 голос
/ 03 июля 2011

Возможное решение: вывести все ваши исходные классы из

class Transaction {
  pthread_mutex_t mtx;
  // constructor/destructor
public:
  void beginTransaction() { pthread_mutex_lock(&mtx); } // ERROR CHECKING MISSING
  void endTransaction() { pthread_mutex_unlock(&mtx); } // DO ERROR CHECKING
protected:
  // helper method
  int getSingle(int *ptr)
  { int v; beginTransaction(); v=*ptr; endTransaction(); return v; }
};

Если вам нужно прочитать несколько значений, используйте методы begin / endTransaction. Чтобы определить ваши функции getValue, просто вызовите getSingle с указателем на соответствующий член [это просто удобный метод, чтобы вам не приходилось вызывать begin / endTransaction в каждой функции getValue.].

Вам нужно будет уточнить некоторые детали, потому что если ваши функции getValue используют begin / endTransaction, вы не сможете вызывать их внутри транзакции. (Мьютекс может быть заблокирован только один раз, если он не настроен на рекурсию.)

...