22 Возможности библиотеки Task Parallel Library. Средства синхронизации среды .NET Framework

Синхронизация необходима для координации выполнения потоков. Такая координация необходима для согласования порядка выполнения потоков или для согласования доступа потоков к разделяемому ресурсу.

Среда .NET Framework предоставляет широкий набор средств синхронизации.

В основе синхронизации лежит понятие блокировки – один поток блокируется в ожидании определенного события от других потоков, например, завершения работы определенного потока или освобождения разделяемого ресурса.


Ожидание может быть активным или пассивным. При активном «ожидании» поток циклически проверяет статус ожидаемого события.

Thread thr = new Thread(SomeWork); 
thr.Start(); 
while(thr.IsAlive) ; 

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

Пассивное ожидание реализуется с помощью операционной системы, которая сохраняет контекст потока и выгружает его, предоставляя возможность выполняться другим потокам. При наступлении ожидаемого события операционная система «будит» поток – загружает контекст потока и выделяет ему процессорное время. Пассивное ожидание требует времени на сохранение контекста потока при блокировке и загрузку контекста при возобновлении работы потока, но позволяет использовать вычислительные ресурсы во время ожидания для выполнения других задач.

В следующем фрагменте используются два типа ожидания. В первом случае применяется циклическая проверка статуса. Во втором случае используется метод Join.

class Program 
{ 
  static bool b; 
  static double res; 
  static void SomeWork() 
  { 
    for (int i=0; i<100000; i++) 
      for(int j=0; j<20; j++) 
      res += Math.Pow(i, 1.33); 
    b = true; 
  } 
  static void Main() 
  { 
    Thread thr1 = new Thread(SomeWork); 
    thr1.Start(); 
    // Активное ожидание в цикле 
    while(!b) ; 
    Console.WriteLine("Result = " + res); 

    res = 0; 
    Thread thr2 = new Thread(SomeWork); 
    thr2.Start(); 
    // Ожидание с выгружением контекста 
    thr2.Join(); 

  } 
} 

Анализ выполнения программы с помощью инструмента Visual Studio 12 «Визуализатор параллелизма» позволяет зафиксировать особенности загрузки вычислительной системы.

Загрузка процессора при активном ожидании в среднем равна 92%.

При пассивном ожидании, основной поток выгружается и занятость ЦП в среднем равна 50%.

Существуют гибридные средства синхронизации, сочетающие в себе достоинства активного и пассивного ожидания. Гибридную блокировку используют объекты синхронизации, введенные в .NET 4.0: 
  • SpinWait, 
  • SpinLock, 
  • SemaphoreSlim, 
  • ManualResetEventSlim, 
  • ReaderWriteLockSlim 
  • и др. 

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