2016-10-25 10 views
1

Конфигурация моего приложения представляет собой объект const, который совместно используется несколькими потоками. Конфигурация хранится в централизованном месте, и любой поток может достичь этого. Я попытался создать реализацию lockfree, которая позволила бы мне загружать новую конфигурацию, позволяя другим потокам читать последнюю известную конфигурацию.Lockfree Перезагрузка и совместное использование объектов const

В моей текущей реализации есть гонка между обновлением shared_ptr и чтением с нее.

template<typename T> 
class ConfigurationHolder 
{ 
public: 
    typedef std::shared_ptr<T> SPtr; 
    typedef std::shared_ptr<const T> CSPtr; 

    ConfigurationHolder() : m_active(new T()) {} 

    CSPtr get() const { return m_active; } // RACE - read 

    template<typename Reloader> 
    bool reload(Reloader reloader) 
    { 
     SPtr tmp(new T()); 
     if (!tmp) 
      return false; 
     if (!reloader(tmp)) 
      return false; 
     m_active=tmp; // RACE - write 
     return true; 
    } 

private: 
    CSPtr m_active; 
}; 

Я могу добавить shared_mutex для проблемного доступа на чтение/запись к shared_ptr, но я ищу решение, которое будет держать lockfree реализации.

EDIT: Моя версия GCC не поддерживает atomic_exchange на shared_ptr

edit2: Требования Разъяснения: У меня есть несколько читателей, и может иметь несколько перегружателей (хотя это менее распространено). Читателям необходимо удержать объект конфигурации и что он не изменится во время чтения. Старые объекты конфигурации должны быть освобождены, когда последний читатель будет с ними выполнен.

+0

Можете ли вы разработать прецедент? Если у вас много читателей, и один поток решает перезагрузить, то 1) вам нужен семафор, а не мьютекс, и 2) Когда другие потоки начнут читать новое значение? Это четко не определено до тех пор, пока они не прочитают старый. – kabanus

+0

@kabanus Отредактировано с пояснениями. Кроме того, я хотел бы избежать добавления мьютексов/семафоров, если это возможно. – Shloim

+0

Вы точно описываете семафор чтения. Операции чтения бесплатны, и запись может быть выполнена только тогда, когда семафор ясен. Все, что вы реализуете с помощью счетчиков, будет именно этим. Предполагая, что назначение указателя не является атомарным (оно может быть с std :: atomic ), вам нужно сделать r/w семафор. Я могу показать вам одну из них, иначе у меня нет решения. – kabanus

ответ

1

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

В противном случае оберните его в shared_timed_mutex. Затем проверьте, сколько это вам стоит.

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

Если у вас есть:


Это хак, но он может работать. Это стиль чтения-копирования-обновления на самом указателе.

Имейте std::vector<std::unique_ptr<std::shared_ptr<T>>>. У вас есть std::atomic<std::shared_ptr<T> const*> «текущий» указатель и std::atomic<std::size_t> active_readers.

vector сохраняет ваши еще живые shared_ptr s. Когда вы хотите изменить, нажмите новый на задней панели. Сохраните копию этого shared_ptr.

Теперь замените «текущий» указатель на новый. Занято - подождите, пока active_readers не достигнет нуля, или пока вам не станет скучно.

Если active_readers ударил нуль, отфильтруйте ваш vector за shared_ptr с использованием -1. Извлеките их из vector.

Независимо от того, теперь отбросьте дополнительные shared_ptr вы ketp к состоянию, которое вы создали. И сделал письмо.

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

Со стороны считывателя, приращение active_readers. Теперь атомно загрузите «текущий» указатель, сделайте локальную копию заостренного на shared_ptr, а затем уменьшите active_readers.

Однако я просто написал это. Поэтому он, вероятно, содержит ошибки. Докажите, что совпадающий дизайн правильный: hard.

Самым простым способом сделать это надёжным является обновление вашего компилятора и получение атомных операций на shared_ptr.


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


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


Практически, алгоритмы безблокировочного часто медленнее, чем замок на основе, так как накладные расходы мьютекса не так высоко, как страх.

A shared_timed_mutex обертывание shared_ptr, где писатель просто перезаписывает переменную, будет довольно быстро проклят. Существующие читатели собираются сохранить свой старый shared_ptr просто отлично.