C++: библиотека программиста



Download 1,95 Mb.
Pdf ko'rish
bet88/144
Sana24.02.2022
Hajmi1,95 Mb.
#223123
TuriРеферат
1   ...   84   85   86   87   88   89   90   91   ...   144
Bog'liq
C -Eldjer-Djeff-for-Real-Programmers-RUS-www.itlibitum.ru

 
153 

b; 
// 
Внедренный объект 
}; 
Компилятор будет автоматически вызывать конструктор копий 
B
при каждом копировании 
A
, и 
оператор 
=
класса 
B
— при выполнении присваивания. 
Предположим, класс 
A
должен заблокировать 
b
. Откуда берется 
Transaction*
? Разумеется, извне 
A
— то есть от клиента. Впрочем, эту задачу можно решить тремя способами: 
• 
Передавать 
Transaction*
в каждую функцию 
A
, которой может потребоваться вызвать 
неконстантную функцию 
B

• 
Один раз передать 
Transaction*
в 
A
во время блокировки и сохранить в переменной класса. 
• 
Сделать указатель 
ConstPtr
класса 
A
еще более гениальным — научить его блокировать 
вторичные объекты по поручению 
A

На последнем решении следует повесить табличку: «Только для профессиональных каскадеров. Не 
уверен — не пытайся». Тем не менее, в некоторых ситуациях лучшая реализация транзакций 
подразумевает использование коммерческих классов указываемых объектов. Этой стратегией можно 
воспользоваться даже для того, чтобы разрешить 
A
хранить его обожаемый 
B*
и ничего не знать о 
ConstPtr
и 
LockPtr
. Поумневшие 
ConstPtr
и/или 
LockPtr
класса 
A
могут без его ведома менять 
адреса при создании образов и закреплении изменений. В дальнейшем мы еще неоднократно 
встретимся с концепцией объектов, которые описывают другие объекты и/или манипулируют ими. 
Ближе к концу мы снова вернемся к проблеме межобъектных ссылок. 
Взаимные блокировки и очереди 
Если в любой момент времени может существовать не более одной транзакции, все замечательно. Если 
же с вашими объектами могут возиться сразу несколько транзакций, придется сделать еще несколько 
шагов. Возможны разные причины — например, ваше приложение-сервер может обслуживать 
несколько пользователей, или вы напишете графическое приложение с контекстной отменой, в 
котором необходимо запоминать отменяемую команду для нескольких окон. Наконец, остается еще 
одно препятствие — необходимо продумать поведение вашей программы в ситуации, когда она 
пытается заблокировать уже заблокированный объект. 
Консервативная блокировка 
При консервативной блокировке (conservative locking) транзакция блокирует все объекты, которые ей 
могут понадобиться, до внесения каких-либо изменений. Если заблокировать все объекты не удается, 
транзакция либо ждет, пока они станут доступными, либо поднимает руки и сообщает пользователю, 
что при всем уважении к нему просьба отклоняется. Одна из возможных реализаций консервативной 
блокировки заключается в том, чтобы субклассировать 
Transaction
и попытаться заблокировать все 
необходимое в конструкторе производного класса (при наличии стандартной обработки исключений) 
или в его отдельной инициализирующей функции (при отсутствии такой обработки). 
Агрессивная блокировка 
При агрессивной блокировке (aggressive locking) транзакция может начинаться в любой момент и 
блокировать объекты по мере необходимости. Я бы назвал ее «своевременной» блокировкой, 
поскольку блокируются только непосредственно обновляемые объекты и это происходит перед первым 
обновлением. 
Очереди и взаимные блокировки 
Вопрос о том, стоит ли создавать очереди запросов на блокировку, составляет отдельный аспект 
дизайна. Если очереди не поддерживаются и транзакция попытается заблокировать объект, ранее 
заблокированный другой транзакцией, она заканчивается неудачей. При консервативной блокировке 
транзакция вообще не начинается, а при аггрессивной она возвращается к прежнему состоянию, 
скрещивает руки на своей двоичной груди и сурово смотрит на пользователя. В некоторых 
приложениях это вполне нормально, но в действительности это решение из тех, о которых специалисты 
из службы поддержки могут лишь мечтать — ведь оно гарантирует им постоянную работу. Чтобы не 


 154 
отказываться от второй транзакции, обычно стоит подождать снятия блокировки с объекта текущей 
транзакцией. 
При отсутствии очередей вам не придется беспокоиться о ситуациях взаимной блокировки (когда 
A
ожидает 
B
, а 
B
ожидает 
A
). Если транзакция запросила блокировку и не смогла ее получить, она 
уничтожается. Если очереди поддерживаются, код блокировки должен определить, принадлежит ли 
этой транзакции какие-либо блокировки, которых дожидаются остальные, и если принадлежат, 
избавить одну из транзакций от бескенечного ожидания. 
Происходящее оказывается в опасной близости от точки, в которой вам приходится либо разбивать 
свое приложение на несколько подзадач и поручать операционной системе их планирование, либо 
воспользоваться одной из коммерческих библиотек с поддержкой многопоточности. Так или иначе, в 
этой области мы не узнаем ничего принципиально нового, относящегося к С++, и поэтому не будем 
развивать эту тему. Об очередях, блокировках и многопоточности написано немало хороших книг, вы 
наверняка найдете их в разделе «Базы данных» своего книжного магазина. 
Многоуровневая отмена 
Семантика транзакций довольно легко распространяется и на многоуровневую отмену (если вспомнить 
концепции 
StackPtr
, о которых говорилось выше). Существует два основных варианта реализации. 
Класс LockPtr со стеками образов 
Самый прямолинейный вариант реализации многоуровневой отмены для транзакций — включение 
стека старых образов в каждый 
LockPtr
. Эта идея позаимствована из рассмотренного выше кода 
StackPtr
. Тем не менее, она подходит лишь для консервативной блокировки. В случае с агрессивной 
блокировкой объект может быть впервые заблокирован уже после изменения остальных объектов. Это 
усложняет отмену нескольких изменений, поскольку стеки разных 
LockPtr
не синхронизируются. 
Стеки пар LockPtr/образ 
К проблеме можно подойти и иначе — включить в 
Transaction
стек, в котором хранятся пары старый 
образ/
LockPtr
. На каждом уровне стека хранятся лишь те 
LockPtr
, которые существовали на момент 
закрепления. В общем случае это решение работает лучше, к тому же оно чуть более эффективно — вы 
используете один большой стек вместо множества маленьких. 
Оптимизация объема 
Другое решение — реорганизация структур данных для сокращения издержек, связанных с хранением 
незаблокированных 
ConstPtr
(хотя и ценой некоторой потери скорости). 
ConstPtr
лишь 
незначительно отличается от указателей только для чтения, которые рассматривались в главе 6 и не 
имели ничего общего с транзакциями: он имеет ссылку на 
ConstPtr
и функцию 
Lock()
. Мы 
избавимся от первого и внесем некоторые изменения во второе. 
Представьте себе глобальную структуру данных (вероятно, хранящуюся в виде статического члена 
класса 
Transaction
), в которой находится информация о том, какие 
Transaction
блокируют какие 
ConstPtr
. Для каждой пары в таблице содержится соответствующий 
LockPtr
. Каждый раз, когда 
вызывается функция 
Lock()
класса 
ConstPtr
, она проверяет, присутствует ли 
this
в таблице. Если 
присутствует, функция сравнивает транзакцию, переданную в качестве аргумента, с находящейся в 
таблице. Если 
ConstPtr
не находит себя в таблице, он включает в нее новый триплет (
ConstPtr

Transaction

LockPtr
), а если находит с другой транзакцией — инициирует исключение. 
Такая схема оказывается более экономной, чем описанная выше; она не тратит память на значения 
NULL
для всех незаблокированных объектов. Разумеется, она сложенее и медленнее работает — 
структура данных еще только разогревает двигатель на старте, а косвенной обращение через 
переменную класса уже пересекает финишную черту. 
Возможно, у вас возник вопрос — а почему функция 
Lock()
должна оставаться с 
ConstPtr
? Почему 
ее нельзя включить в другой класс или даже сделать глобальной функцией? Если мы избавимся от 
переменной 
LockPtr*
и функции 
Lock()

ConstPtr
превратится в самый обычный указатель только 
для чтения, который на вопрос о транзакциях лишь тупо смотрит на вопрошающего. Впрочем, так ли 
это? 
LockPtr
по-прежнему приходится объявлять другом; следовательно, хотя бы тривиальных 



Download 1,95 Mb.

Do'stlaringiz bilan baham:
1   ...   84   85   86   87   88   89   90   91   ...   144




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©hozir.org 2024
ma'muriyatiga murojaat qiling

kiriting | ro'yxatdan o'tish
    Bosh sahifa
юртда тантана
Боғда битган
Бугун юртда
Эшитганлар жилманглар
Эшитмадим деманглар
битган бодомлар
Yangiariq tumani
qitish marakazi
Raqamli texnologiyalar
ilishida muhokamadan
tasdiqqa tavsiya
tavsiya etilgan
iqtisodiyot kafedrasi
steiermarkischen landesregierung
asarlaringizni yuboring
o'zingizning asarlaringizni
Iltimos faqat
faqat o'zingizning
steierm rkischen
landesregierung fachabteilung
rkischen landesregierung
hamshira loyihasi
loyihasi mavsum
faolyatining oqibatlari
asosiy adabiyotlar
fakulteti ahborot
ahborot havfsizligi
havfsizligi kafedrasi
fanidan bo’yicha
fakulteti iqtisodiyot
boshqaruv fakulteti
chiqarishda boshqaruv
ishlab chiqarishda
iqtisodiyot fakultet
multiservis tarmoqlari
fanidan asosiy
Uzbek fanidan
mavzulari potok
asosidagi multiservis
'aliyyil a'ziym
billahil 'aliyyil
illaa billahil
quvvata illaa
falah' deganida
Kompyuter savodxonligi
bo’yicha mustaqil
'alal falah'
Hayya 'alal
'alas soloh
Hayya 'alas
mavsum boyicha


yuklab olish