Чтение онлайн

на главную

Жанры

Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:

……

public class Account: MarshalByRefObject,

IAccumulator, IAudit {

…….

[MethodImpl(MethodImplOptions.Synchronized)]

public void Add(int sum) {

_sum += sum;

}

…….

}

}

Заметим, ЧТО теперь достаточно наследования класса Account от класса MarshalByRefObject, так как привязка экземпляра этого класса к контексту более не нужна.

Использование атрибута [MethodImpl (MethodImplOptions.Synchronized)] конечно удобно, однако и накладывает на программиста определенные ограничения:

• Критическая секция охватывает все тело метода

Можно представить

ситуацию, когда критичная операция, требующая защиты от параллельного выполнения в нескольких потоках, выполняется в данном методе только в некоторых случаях (в зависимости от значений входных аргументов). В этом случае включение всего метода в критическую секцию неоправдано и будет снижать общую эффективность системы.

• Нет возможности запретить параллельный доступ к совместно используемым объектам Предположим, в данном методе выполняется работа с некоторой очередью (экземпляр класса Queue). Конечно, благодаря наличию атрибута [MethodImpl (MethodImplOptions.Synchronized)] В рамках данного метода два потока не смогут параллельно работать с этой очередью и целостность данных будет обеспечена. Однако, ничто не запрещает какому-то другому потоку обратиться к этой же самой очереди в процессе выполнения какого-либо другого метода. Вот тут и возможны нарушения целостности, т. к. между различными потоками, выполняющими параллельно различные методы, нет никакой коммуникации.

Указанные выше проблемы решаются при использовании класса Monitor.

……

namespace MyServer {

…….

public class Account: MarshalByRefObject,

IAccumulator, IAudit {

……

public void Add(int sum) {

…….

Monitor.Enter(this);

try {

_sum += sum;

}

finally {

Monitor.Exit(this);

}

……

}

……

}

}

Вызов статического метода Monitor.Enter помечает начало критической секции, а вызов метода Monitor.Exit — ее конец. Аргумент в методе Enter представляет собой ссылку на некоторый объект. В данном случае это ссылка на экземпляр класса Account, на котором и вызван метод Enter, однако ничто не мешает указать ссылку на какой-либо другой объект.

Объект, на который указывает ссылка при вызове Enter, начинает играть роль "эстафетной палочки". Поток, которому удалось вызвать Monitor.Enter (obj), входит в данную критическую секцию, и никакой другой поток не получит ответа от вызова Monitor.Enter (obj), пока первый поток не вызовет Monitor.Exit (obj). Все потоки, сделавшие вызов Monitor.Enter (obj), находятся в одной очереди потоков готовых к выполнению, и эта очередь связана с объектом obj.

Использование блока try и включение вызова Monitor.Exit (obj) в блок finally способствует повышению надежности программирования. Если даже после входа в критическую секцию будет сгенерировано какое-то исключение, вызов Monitor.Exit (obj) будет выполнен в любом случае, и очередной готовый к выполнению поток, заблокированный при вызове Monitor.Enter (obj), начнет выполняться.

Хотя, как указывалось ранее, в качестве "эстафетной палочки" можно использовать любой объект, разумно использовать именно тот объект, ради безопасного доступа к которому и была сформирована данная критическая секция. В этом случае (если такой же подход будет использован при формировании всех критических секций) два различных потока не будут параллельно выполнять критичные для целостности данных операции над одним и тем же объектом.

Компилятор для C# допускает использование конструкции lock (obj) {} для задания критической секции. При этом неявно используется тот же класс Monitor:

……

namespace MyServer {

…….

public class Account: MarshalByRefObject,

IAccumulator, IAudit {

…….

public void Add(int sum) {

lock(this) {

_sum += sum;

}

}

…….

}

}

Имеются еще два метода класса Monitor, которые используются в коде атрибута синхронизации. Это Monitor.Wait и Monitor.Pulse .

Рассмотрим следующую модификацию предыдущего примера:

…….

namespace MyServer {

…….

public class Account: MarshalByRefObject,

IAccumulator, IAudit {

…….

public void Add(int sum) {

lock(this) {

Console.WriteLine (Thread.CurrentThread.GetHashCode };

int s = _sum;

Thread.Sleep(1);

_sum = s + sum;

if (_sum == 5) {Monitor.Wait(this);}

if (_sum == 505) {Monitor.Pulse(this);}

}

}

……

}

Напомним, что данный фрагмент кода выполняется на сервере MyServer.ехе, к которому параллельно могут обращаться несколько клиентов. Каждый клиент (приложение MуАрр) посылает на сервер 100 раз по 5 условных единиц.

Выводя на консоль хеш потока, мы можем отследить чередование рабочих потоков в очереди готовых к выполнению потоков. Сохранение текущей величины счета в локальной переменной s и вызов Thread.Sleep (1) используются для более явного выявления эффектов, связанных с многопоточностью.

Как правило (если в предыдущем фрагменте кода закомментировать строки с вызовами Monitor.Wait и Monitor.Pulse), один и тот же поток может несколько раз подряд войти в данную критическую секцию и положить на счет очередные 5 условных единиц, прежде чем выделенный ему квант времени закончится и начнет исполняться другой рабочий поток. После нескольких циклов вновь начинает работать первый поток и так далее. Используя методы Wait и Pulse класса Monitor мы можем управлять очередностью входа различных потоков в данную критическую секцию.

Как только первый поток входит в нашу критическую секцию, он выводит на консоль свой идентификатор, запоминает текущее значение счета в локальной переменной и засыпает на 1 миллисекунду. Пробудившись, этот поток обновляет счет (его величина становится равной 5).

В связи с выполнением условия _sum == 5 выполняется вызов Monitor.Wait (this). В этот момент первый поток освобождает объект this и становится в очередь ожидания. Эта еще одна, связанная с объектом очередь (наряду с очередью потоков, готовых к выполнению). Разница между ними состоит в следующем. Очередной поток из очереди готовых к выполнению потоков начинает выполняться, если текущий исполняемый поток завершил выполнение критической секции (вызвал Monitor.Exit (this), то есть освободил объект this). Потоки из очереди ожидания становятся в очередь потоков готовых к выполнению, если текущий исполняемый поток вызвал Monitor.Pulse (this), сигнализируя тем самым, что состояние объекта this изменилось и ожидающие потоки могут работать с данным объектом.

Поделиться:
Популярные книги

Я — Легион

Злобин Михаил
3. О чем молчат могилы
Фантастика:
боевая фантастика
7.88
рейтинг книги
Я — Легион

Совок – 3

Агарев Вадим
3. Совок
Фантастика:
фэнтези
детективная фантастика
попаданцы
7.92
рейтинг книги
Совок – 3

Иван Московский. Первые шаги

Ланцов Михаил Алексеевич
1. Иван Московский
Фантастика:
героическая фантастика
альтернативная история
5.67
рейтинг книги
Иван Московский. Первые шаги

Чехов. Книга 3

Гоблин (MeXXanik)
3. Адвокат Чехов
Фантастика:
альтернативная история
5.00
рейтинг книги
Чехов. Книга 3

Последний попаданец 3

Зубов Константин
3. Последний попаданец
Фантастика:
фэнтези
юмористическое фэнтези
рпг
5.00
рейтинг книги
Последний попаданец 3

Оживший камень

Кас Маркус
1. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Оживший камень

Сумеречный Стрелок 3

Карелин Сергей Витальевич
3. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Сумеречный Стрелок 3

Хозяйка Междуречья

Алеева Елена
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Хозяйка Междуречья

Жребий некроманта 2

Решетов Евгений Валерьевич
2. Жребий некроманта
Фантастика:
боевая фантастика
6.87
рейтинг книги
Жребий некроманта 2

Попаданка в академии драконов 4

Свадьбина Любовь
4. Попаданка в академии драконов
Любовные романы:
любовно-фантастические романы
7.47
рейтинг книги
Попаданка в академии драконов 4

Черный маг императора

Герда Александр
1. Черный маг императора
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Черный маг императора

Чехов. Книга 2

Гоблин (MeXXanik)
2. Адвокат Чехов
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Чехов. Книга 2

Ваше Сиятельство 4т

Моури Эрли
4. Ваше Сиятельство
Любовные романы:
эро литература
5.00
рейтинг книги
Ваше Сиятельство 4т

Беглец

Кораблев Родион
15. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Беглец