Материалы книги получены с http://www.itlibitum.ru/
Вложенные блокировки
Чтобы вся методика имела хоть какой-то смысл, мы должны спрятать ключи от указываемых объектов и заставить всех работать с ContrPtr и LockPtr. Это относится не только к изменениям, вносимым в указываемые объекты непосредственно клиентами, но и к тем, которые один указываемый объект вносит в другой. Таким образом, в общем случае указываемые объекты должны обращаться друг к другу через ConstPtr и LockPtr, как и клиенты.
class A {
private:
ConstPtr<B>& b; // ConstPtr, как выше
// И т.д.
};
Если экземпляр класса A захочет вызвать неконстантную функцию b, он должен сначала получить LockPtr для b. Таким образом, класс A должен знать, какую транзакцию он выполняет при попытке изменения b, и эта информация должна поступить от клиента. Для простоты назовем объекты, к которым клиенты обращаются напрямую, первичными, а объекты, косвенно обновляемые указываемым объектом, - вторичными по отношению к указываемому объекту (один и тот же объект может одновременно быть и первичным, и вторичным).
Если вторичный объект инкапсулируется в первичном (то есть вторичный объект невидим для окружающего мира), это правило можно обойти. Конструктор копий первичного объекта должен продублировать вторичный объект, а оператор = первичного объекта должен продублировать вторичный объект правостороннего выражения. При соблюдении этих условий логика создания образов в ConstPtr и LockPtr будет правильно работать с инкапсулированными объектами. Это происходит автоматически в тех случаях, когда вторичный объект внедряется в первичный (то есть когда в каждом экземпляре A объект B хранится не как указатель, а как обычная переменная класса):
class A {
private:
B b; // Внедренный объект
};
Компилятор будет автоматически вызывать конструктор копий B при каждом копировании A, и оператор = класса B - при выполнении присваивания.
Предположим, класс A должен заблокировать b. Откуда берется Transaction*? Разумеется, извне A - то есть от клиента. Впрочем, эту задачу можно решить тремя способами:
1. Передавать Transaction* в каждую функцию A, которой может потребоваться вызвать неконстантную функцию B.
2. Один раз передать Transaction* в A во 0 td0 Tcвремя блокировки и сохранить в переменной класса
Назад Содержание Далее
|