838
Глава.29 .Примитивные.конструкции.синхронизации.потоков
// Если поток переводит его из свободного состояния,
// вернуть управление
if (Interlocked.Exchange(ref m_ResourceInUse, 1) == 0) return;
// Здесь что-то происходит...
}
}
public void Leave() {
// Помечаем ресурс, как свободный
Volatile.Write(ref m_ResourceInUse, 0);
}
}
А вот класс, демонстрирующий использование метода
SimpleSpinLock
:
public sealed class SomeResource {
private SimpleSpinLock m_sl = new SimpleSpinLock();
public void AccessResource() {
m_sl.Enter();
// Доступ к ресурсу в каждый момент времени имеет только один поток...
m_sl.Leave();
}
}
Реализация метода
SimpleSpinLock
очень проста. Если два потока одновременно
вызывают
метод
Enter
, метод
Interlocked.Exchange
гарантирует, что один поток
изменит значение переменной
m_resourceInUse
с 0 на 1. Когда он видит, что пере-
менная
m_resourceInUse
равна 0, он
заставляет метод
Enter
возвратить управление,
чтобы продолжить выполнение кода метода
AccessResource
. Второй поток тоже
попытается заменить значение переменной
m_resourceInUse
на 1. Но этот поток
увидит, что переменная уже не равна 0, и зациклится,
начав непрерывно вызывать
метод
Exchange
до тех пор, пока первый поток не вызовет метод
Leave
.
После того как первый поток завершит манипуляции полями объекта
SomeResource
, он вызовет метод
Leave
, который, в свою очередь, вызовет метод
Volatile.Write
и
вернет переменной
m_resourceInUse
значение 0. Это заставит
зациклившийся поток поменять значение переменной
m_resourceInUse
с 0 на 1 и,
наконец, получить управление от метода
Enter
, предоставляя последнему доступ
к полям объекта
SomeResource
.
Вот и все. Это очень простая реализация блокирования в
рамках синхронизации
потоков. Правда, ее серьезный потенциальный недостаток состоит в том, что при
наличии конкуренции за право на блокирование потоки вынуждены ожидать блоки-
рования в цикле, и это зацикливание приводит к пустому расходованию бесценного
процессорного времени. Соответственно, блокирование с зацикливанием имеет
смысл использовать только для защиты очень быстро выполняемых областей кода.
Блокирование с зацикливанием обычно не применяется на машинах с одним
процессором, так как зацикливание другого потока-претендента помешает бы-
строму снятию блокировки. Ситуация осложняется,
если поток, удерживающий
839
Конструкции.пользовательского.режима
блокировку, имеет более низкий приоритет, чем поток, претендующий на ее
получение. Низкоприоритетный поток может вообще не получить шансов на вы-
полнение, то есть просто зависнуть. Windows иногда на
короткое время динами-
чески повышает приоритет потоков. Для потоков, использующих блокирование
с зацикливанием, данный режим следует отключить. Это делается при помощи
свойств
PriorityBoostEnabled
классов
System.Diagnostics.Process
и
System.
Diagnostics.ProcessThread
. Блокирование с зацикливанием на гиперпотоковых
машинах также связано с проблемами. Для их решения код блокирования с за-
цикливанием часто наделяется дополнительной логикой.
Однако я не хотел бы
вдаваться в детали, так как эта логика довольно быстро меняется. Могу сказать
только, что FCL поставляется вместе со структурой
System.Threading.SpinWait
,
которая заключает в себя всю необходимую логику.
Do'stlaringiz bilan baham: