Составители:
Рубрика:
private int m_a = 0, m_b = 0;
public int summ() {
int r;
m_rwlock.AcquireReaderLock( -1 );
try {
r = m_a; Thread.Sleep( 1000 ); return r + m_b;
} finally {
m_rwlock.ReleaseReaderLock();
}
}
public int inc() {
m_rwlock.AcquireWriterLock( -1 );
try {
m_a++; Thread.Sleep( 500 ); m_b++;
return m_a + m_b;
} finally {
m_rwlock.ReleaseWriterLock();
}
}
public static void Invoke( SomeData sd, int no ) {
if ( no % 2 == 0 ) {
Console.WriteLine( sd.inc() );
} else {
Console.WriteLine( sd.summ() );
}
}
}
public delegate void AsyncProcCallback(SomeData sd, int no);
class TestApp {
public static void Main() {
int i;
SomeData sd = new SomeData();
WaitHandle[] wh;
AsyncProcCallback apd;
wh = new WaitHandle[ SomeData.m_queries ];
apd = new AsyncProcCallback( SomeData.Invoke );
for ( i = 0; i < SomeData.m_queries; i++ ) wh[i] =
apd.BeginInvoke(sd,i,null,null).AsyncWaitHandle;
WaitHandle.WaitAll( wh );
}
}
}
Разработка параллельных приложений для ОС Windows
269
Приведенный пример показывает синхронизацию с использованием
мьютекса, события с ручным сбросом и объекта WaitHandle, представляюще-
го состояние асинхронного вызова. В примере делается 10 асинхронных вы-
зовов, после чего приложение ожидает завершения всех вызовов с помощью
метода WaitAll. Каждый асинхронный метод в секции кода, защищаемой
мьютексом (здесь было бы эффективнее использовать монитор или блоки-
ровку), подсчитывает число сделанных вызовов и переходит к ожиданию за-
нятого события. Самый последний асинхронный вызов установит событие в
свободное состояние, после чего все вызовы должны завершиться.
Помимо использования разных синхронизирующих объектов, в этом
примере интересно поведение CLR: асинхронные вызовы должны обраба-
тываться в пуле потоков, однако число вызовов превышает число потоков
в пуле. CLR по мере необходимости добавляет в пул потоки для обработ-
ки поступающих запросов.
Потоки не являются наследниками класса WaitHandle в силу того, что
для разных базовых платформ потоки могут быть реализованы в качестве
потоков операционной системы или легковесных потоков, управляемых
CLR. В последнем случае потоки .NET не будут иметь никаких аналогов
среди объектов ядра операционной системы. Для синхронизации с пото-
ками надо использовать метод Join класса Thread.
Один «писатель», много «читателей»
Одной из типичных задач синхронизации потоков является задача, в
которой допускается одновременный конкурентный доступ многих объе-
ктов для чтения данных («читатели») и исключительный доступ единст-
венного потока, вносящего в объект изменения («писатель»). В Win32 API
стандартного объекта, реализующего подобную логику, не существует, по-
этому каждый раз его надо проектировать и создавать заново.
.NET предоставляет весьма эффективное стандартное решение:
класс ReaderWriterLock. В приводимом ниже примере демонстрируется
применение методов Acquire... и Release... для корректного использова-
ния блокировки доступа при чтении и записи. Тестовый класс содержит
две целочисленные переменные, которые считываются и увеличиваются
на 1 с небольшими задержками по отношению друг к другу. Пока опера-
ции синхронизируются, попытка чтения или изменения всегда будет воз-
вращать четный результат, а вот если бы синхронизация не выполнялась,
то в некоторых случаях получались бы нечетные числа:
using System;
using System.Threading;
namespace TestNamespace {
public class SomeData {
public const int m_queries = 10;
private ReaderWriterLock m_rwlock = new ReaderWriterLock();
268
CIL и системное программирование в Microsoft .NET
268 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 269 Приведенный пример показывает синхронизацию с использованием private int m_a = 0, m_b = 0; мьютекса, события с ручным сбросом и объекта WaitHandle, представляюще- public int summ() { го состояние асинхронного вызова. В примере делается 10 асинхронных вы- int r; зовов, после чего приложение ожидает завершения всех вызовов с помощью m_rwlock.AcquireReaderLock( -1 ); метода WaitAll. Каждый асинхронный метод в секции кода, защищаемой try { мьютексом (здесь было бы эффективнее использовать монитор или блоки- r = m_a; Thread.Sleep( 1000 ); return r + m_b; ровку), подсчитывает число сделанных вызовов и переходит к ожиданию за- } finally { нятого события. Самый последний асинхронный вызов установит событие в m_rwlock.ReleaseReaderLock(); свободное состояние, после чего все вызовы должны завершиться. } Помимо использования разных синхронизирующих объектов, в этом } примере интересно поведение CLR: асинхронные вызовы должны обраба- public int inc() { тываться в пуле потоков, однако число вызовов превышает число потоков m_rwlock.AcquireWriterLock( -1 ); в пуле. CLR по мере необходимости добавляет в пул потоки для обработ- try { ки поступающих запросов. m_a++; Thread.Sleep( 500 ); m_b++; Потоки не являются наследниками класса WaitHandle в силу того, что return m_a + m_b; для разных базовых платформ потоки могут быть реализованы в качестве } finally { потоков операционной системы или легковесных потоков, управляемых m_rwlock.ReleaseWriterLock(); CLR. В последнем случае потоки .NET не будут иметь никаких аналогов } среди объектов ядра операционной системы. Для синхронизации с пото- } ками надо использовать метод Join класса Thread. public static void Invoke( SomeData sd, int no ) { Один «писатель», много «читателей» if ( no % 2 == 0 ) { Одной из типичных задач синхронизации потоков является задача, в Console.WriteLine( sd.inc() ); которой допускается одновременный конкурентный доступ многих объе- } else { ктов для чтения данных («читатели») и исключительный доступ единст- Console.WriteLine( sd.summ() ); венного потока, вносящего в объект изменения («писатель»). В Win32 API } стандартного объекта, реализующего подобную логику, не существует, по- } этому каждый раз его надо проектировать и создавать заново. } .NET предоставляет весьма эффективное стандартное решение: public delegate void AsyncProcCallback(SomeData sd, int no); класс ReaderWriterLock. В приводимом ниже примере демонстрируется class TestApp { применение методов Acquire... и Release... для корректного использова- public static void Main() { ния блокировки доступа при чтении и записи. Тестовый класс содержит int i; две целочисленные переменные, которые считываются и увеличиваются SomeData sd = new SomeData(); на 1 с небольшими задержками по отношению друг к другу. Пока опера- WaitHandle[] wh; ции синхронизируются, попытка чтения или изменения всегда будет воз- AsyncProcCallback apd; вращать четный результат, а вот если бы синхронизация не выполнялась, wh = new WaitHandle[ SomeData.m_queries ]; то в некоторых случаях получались бы нечетные числа: apd = new AsyncProcCallback( SomeData.Invoke ); using System; for ( i = 0; i < SomeData.m_queries; i++ ) wh[i] = using System.Threading; apd.BeginInvoke(sd,i,null,null).AsyncWaitHandle; namespace TestNamespace { WaitHandle.WaitAll( wh ); public class SomeData { } public const int m_queries = 10; } private ReaderWriterLock m_rwlock = new ReaderWriterLock(); }
Страницы
- « первая
- ‹ предыдущая
- …
- 139
- 140
- 141
- 142
- 143
- …
- следующая ›
- последняя »