862
Глава.30 .Гибридные.конструкции.синхронизации.потоков
рис. 30.1.
.Индекс.блоков.синхронизации.объектов.в.куче.(включая.объекты-типы).
может.ссылаться.на.запись.в.массиве.блоков.синхронизации.CLR
Следующий код демонстрирует предполагаемый исходный вариант использо-
вания класса
Monitor
:
internal sealed class Transaction {
private DateTime m_timeOfLastTrans;
public void PerformTransaction() {
Monitor.Enter(this);
// Этот код имеет эксклюзивный доступ к данным...
m_timeOfLastTrans = DateTime.Now;
Monitor.Exit(this);
}
public DateTime LastTransaction {
get {
Monitor.Enter(this);
// Этот код имеет совместный доступ к данным...
DateTime temp = m_timeOfLastTrans;
Monitor.Exit(this);
return temp;
}
}
}
863
Гибридные.конструкции.в.FCL
На первый взгляд все выглядит достаточно просто, но это не так. Проблема в том,
что индекс блока синхронизации каждого объекта неявно находится в открытом
доступе. И вот как это проявляется:
public static void SomeMethod() {
var t = new Transaction();
Monitor.Enter(t); // Этот поток получает открытую блокировку объекта
// Заставляем поток пула вывести время LastTransaction
// ПРИМЕЧАНИЕ. Поток пула заблокирован до вызова
// методом SomeMethod метода Monitor.Exit!
ThreadPool.QueueUserWorkItem(o => Console.WriteLine(t.LastTransaction));
// Здесь выполняется какой-то код...
Monitor.Exit(t);
}
В этом коде поток, выполняющий метод
SomeMethod
, вызывает метод
Monitor.
Enter
, получая открытую блокировку объекта
Transaction
. Когда поток пула за-
прашивает свойство
LastTransaction
, это свойство также вызывает метод
Monitor.
Enter
, чтобы получить право на то же самое блокирование. В результате поток пула
оказывается заблокированным, пока поток, выполняющий метод
SomeMethod
, не
вызовет метод
Monitor.Exit
. При помощи отладчика можно определить, что поток
пула заблокирован внутри свойства
LastTransaction
, но узнать, какой еще поток
заблокирован, очень сложно. Для этого нужно понять, какой именно код привел
к получению блокировки. Но даже если вы это узнаете, вполне может оказаться,
что этот код окажется недоступным для редактирования, а значит, вы не сможете
устранить проблему. Именно поэтому я предлагаю пользоваться только закрытыми
блокировками. Вот каким образом следует исправить класс
Transaction
:
internal sealed class Transaction {
private readonly Object m_lock = new Object(); // Теперь блокирование
// в рамках каждой транзакции ЗАКРЫТО
private DateTime m_timeOfLastTrans;
public void PerformTransaction() {
Monitor.Enter(m_lock); // Вход в закрытую блокировку
// Этот код имеет эксклюзивный доступ к данным...
m_timeOfLastTrans = DateTime.Now;
Monitor.Exit(m_lock); // Выход из закрытой блокировки
}
public DateTime LastTransaction {
get {
Monitor.Enter(m_lock); // Вход в закрытую блокировку
// Этот код имеет монопольный доступ к данным...
DateTime temp = m_timeOfLastTrans;
Monitor.Exit(m_lock); // Завершаем закрытое блокирование
return temp;
}
}
}
864
Do'stlaringiz bilan baham: |