История изменений
Исправление Manhunt, (текущая версия) :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр указателя - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходить умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, о скорости их работы, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_P = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}
Исправление Manhunt, :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр указателя - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходить умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, о скорости их работы, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_P = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}
Исправление Manhunt, :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходить умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, о скорости их работы, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_P = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}
Исправление Manhunt, :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходит умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, о скорости их работы, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_P = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}
Исправление Manhunt, :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходит умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_P = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}
Исходная версия Manhunt, :
Я бы не делал жестко 2 буфера.
Сделал бы один «указатель» (в смысле, один экземпляр - назовём его P), указывающий на память с иммутабельными данными. Доступ к указателю P - под rwlock. Тип указателя P - с семантикой shared_ptr.
Читатель под read lock делает себе копию P и отпускает read lock. Затем спокойно работает с иммутабельными данными безо всякой синхронизации. Пока есть хотя бы одна живая копия указателя, память остается аллоцированной, данные живут. Когда умирает последняя копия указателя, данные разрушаются (синхронизация тут не нужна, т.к. это был последний живой указатель на данные, с ними 100% уже никто не работает), память отпускается.
Писатель аллоцирует память под новые данные, заполняет её в комфортном для себя темпе (синхронизация не нужна, т.к. кроме писателя пока никто не знает об этой памяти). Затем захватывает write lock, делает себе на стек копию указателя P, меняет P так чтобы тот указывал на новые данные, и отпускает write lock. Затем со стека отпускает свою копию старого значения P (если никто из читателей на этот момент не имеет копии старой версии P, то тут будет происходит умирание старых данных и отпускание старой памяти).
Преимущество этой схемы в том, что она не делает никак предположений о времени жизни данных, о количестве читателей и писателей, и тд. Таким образом дальнейшее развитие программы не сковано этими предположениями, и не потребует изменений в этой схеме.
struct Data {....};
using DataP = std::shared_ptr<Data>;
using DataCP = std::shared_ptr<const Data>;
class DataStorage
{
public:
DataCP get()
{
std::shared_lock<std::shared_mutex> read_lock(_rwlock);
return _P;
}
void set(const DataCP &newptr)
{
DataCP oldptr;
{
std::unique_lock<std::shared_mutex> write_lock(_rwloc);
oldptr = _P;
_p = newptr;
}
// release oldptr
}
private:
DataCP _P;
std::shared_mutex _rwlock;
};
void reader_thread(DataStorage &storage)
{
....
DataCP data = storage.get();
....
}
void writer_thread(DataStorage &storage)
{
....
{
DataP data = std::make_shared<Data>(....);
....
storage.set(data);
}
....
}