использовать замок
Перед лицом многопоточного чтения и записи одного и того же фрагмента памяти книга продолжается выше.Должна ли производительность блокировок чтения-записи быть лучше?, предполагая, что мы выбрали блокировку, самый простой подход обычно выглядит следующим образом:
// write thread
{
LockGuard guard(lock);
obj.load(); // load会对obj的属性进行重写
}
// read thread
{
LockGuard guard(lock);
useObj(obj); // useObj会读取obj的属性
}
Однако в этом случае все операции чтения и записи obj будут помещены в блокировку, а критическая секция окажется слишком большой, что окажет большее влияние на параллелизм.
сжать критическую секцию
Чтобы уменьшить критическую секцию, мы часто жертвуем небольшим объемом памяти, пространством в угоду времени:
shared_ptr<Obj> obj;
// write thread
{
shared_ptr<Obj> tmp = std::make_shared<Obj>();
tmp.load();
{
LockGuard guard(lock);
obj = tmp;
}
}
// read thread
{
shared_ptr<Obj> tmp = std::make_shared<Obj>();
{
LockGuard guard(lock);
tmp = obj;
}
useObj(tmp);
}
Теперь мы удалили все критические разделы load и useObj объекта Obj, что означает, что эта часть операции может обеспечить параллелизм.
На самом деле, мы также можем использовать технологию двойного буфера для полной блокировки.
двойной буфер
Так называемая технология двойного буфера фактически предназначена для подготовки двух объектов, одного для чтения и одного для записи. После завершения записи два объекта Obj атомарно заменяются; последующие операции чтения помещаются в обмененный объект чтения, и исходный объект чтения может быть записан снова после завершения исходной «операции чтения».
Однако здесь есть две проблемы:
1.“原子交换”如何做?
2.如何判断,原来的读对象上的读取操作都结束了?
Сначала рассмотрим второй вопрос: вы можете получить его счетчик ссылок с помощью use_count() функции shared_ptr, чтобы определить, читают ли в данный момент этот объект другие потоки;
Однако чтение и запись shared_ptr не могут выполнять атомарные операции——Счетчик ссылок shared_ptr является атомарным, а сам shared_ptr — нет.
В этот момент вы можете передумать. Мы помещаем два объекта shared_ptr в массив и используем атомарный индекс для представления текущего читаемого объекта.В настоящее время для «атомарного обмена» требуются только атомарные индексы присваивания.
Псевдокод выглядит следующим образом:
std::vector<shared_ptr<Obj>> obj_buffers;
std::atomic_size_t curr_idx;
// write thread
{
size_t prepare = 1 - curr_idx.load();
if (obj_buffers[prepare].use_count() > 1) {
continue;
}
obj_buffers[prepare]->load();
curr_idx = prepare;
}
// read thread
{
shared_ptr<Obj> tmp = obj_buffers[curr_idx.load()];
useObj(tmp);
}
Здесь следует отметить, что базовые типы C++ не гарантируют атомарность, поэтому здесь вам нужно использовать новый атомарный тип std::atomic в C++11 в качестве нижнего индекса.
Рекомендуемое чтение:
set_allocated_xxx разминирование в protobuf
Должна ли производительность блокировок чтения-записи быть лучше?
программирование, ориентированное на данные
Пожалуйста, укажите источник:blog.Mandarin.com/2018/03/17/…
Добро пожаловать в WeChat для сканирования приведенного ниже QR-кода. Подпишитесь на мою общедоступную учетную запись WeChat TechTalking, Technology·Life·Thinking:
Бэкенд-технологии «черный дом»