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, но я ищу решение, которое сохранит реализацию без блокировки.

РЕДАКТИРОВАТЬ: моя версия GCC не поддерживает atomic_exchange на shared_ptr

РЕДАКТИРОВАТЬ 2: Разъяснение требований: у меня есть несколько читателей и может иметь несколько перезагружателей (хотя это не так часто). Читатели должны содержать объект конфигурации, и он не будет меняться во время чтения. Старые объекты конфигурации должны быть освобождены, когда последний читатель покончит с ними.

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_ptrs. Если вы хотите изменить, нажмите новый на спине. Сохраните копию этого shared_ptr,

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

Если active_readers ударить ноль, отфильтровать vector за shared_ptrs с счетчиком использования 1. Удалите их из vector,

Независимо, теперь бросьте лишний shared_ptr Вы приближаетесь к состоянию, которое создали. И закончил писать.

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

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

Тем не менее, я только что написал это. Так что, вероятно, содержит ошибки. Подтверждение правильности одновременного проектирования жесткий.

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


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


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


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

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

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector