Вход/Регистрация
Интернет-журнал "Домашняя лаборатория", 2007 №6
вернуться

Журнал «Домашняя лаборатория»

Шрифт:

Использование блока 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 изменилось и ожидающие потоки могут работать с данным объектом.

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

Делегаты, регистрация callback делегата в пуле рабочих потоков

В коде метода InitIfNecessary класса SynchronizationAttribute используются упомянутые в заголовке данного раздела сущности. Познакомимся с их применением в процессе разбора следующего примера:

using System;

using System.Threading;

public class Test {

private AutoResetEvent _myEvent;

private int _count = 0;

public Test {

Console.WriteLine("»> Test constructor thread = " +

Thread.CurrentThread.GetHashCode +

" IsPoolThread = " +

Thread.CurrentThread.IsThreadPoolThread);

_myEvent = new AutoResetEvent(false);

WaitOrTimerCallback myCallBackDelegate =

  • Читать дальше
  • 1
  • ...
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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