Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 139 стр.

UptoLike

будут создавать и множить свои собственные упакованные представления
типов-значений, и никакой синхронизации не произойдет. Поэтому при
использовании мониторов важно проследить, чтобы вызовы разных мето-
дов в разных потоках использовали один общий объект ссылочного типа.
Можно выделить интересный момент – типы объектов сами являют-
ся экземплярами класса Type, и для них выделяется место в управляемой
куче. Это позволяет использовать тип объекта в качестве владельца запи-
си SyncBlock:
...
for ( j = 0; j < m_size; j++ ) {
for ( k = from; k < to; k++ ) {
Monitor.Enter( typeof(double) );
try {
m_C[i,j] += m_A[i,k] * m_B[k,j];
} finally {
Monitor.Exit( typeof(double) );
}
}
}
...
Возможно неявное использование мониторов в C# с помощью клю-
чевого слова lock:
lock ( obj ) { ... }
эквивалентна
Monitor.Enter( obj ); try { ... }
finally { Mointor.Exit( obj ); }
Использование ключевого слова lock предпочтительно, так как при
этом выполняется дополнительная синтаксическая проверка – попытка
использовать для блокировки тип-значение приведет к диагностируемой
компилятором ошибке, вместо трудно отлавливаемой ошибки во время
исполнения:
public static void ThreadProc()
{
int i,j,k, from, to;
double R;
from = (Interlocked.Increment(ref m_stripused) – 1) * m_stripsize;
to = from + m_stripsize;
Разработка параллельных приложений для ОС Windows
265
...
for ( j = 0; j < m_size; j++ ) {
for ( k = from; k < to; k++ ) {
Monitor.Enter( m_C );
try {
m_C[i,j] += m_A[i,k] * m_B[k,j];
} finally {
Monitor.Exit( m_C );
}
}
}
...
В этом фрагменте надо выделить два существенных момента: во-пер-
вых, использование метода Exit в блоке finally, а во-вторых – использо-
вание всего массива m_C, а не отдельного элемента m_C[i,j].
Первое надо взять за правило, так как в случае возникновения ис-
ключения в критической секции блокировка может остаться занятой
.е. в случае покидания секции без вызова метода Exit).
Второе связано с тем, что элементы m_C[i,j] являются значениями, а
не ссылочными типами. Для типов-значений соответствующее представ-
ление в управляемой куче не создается, и у них нет и не может быть ссы-
лок на синхронизирующие записи SyncBlock.
Самое плохое в этой ситуации то, что попытка собрать приложение,
использующее типы-значения в качестве аргументов методов Enter и Exit
(как в примере ниже), пройдет успешно:
...
for ( j = 0; j < m_size; j++ ) {
for ( k = from; k < to; k++ ) {
Monitor.Enter( m_C[i,j] );
try {
m_C[i,j] += m_A[i,k] * m_B[k,j];
} finally {
Monitor.Exit( m_C[i,j] );
}
}
}
...
В прототипах методов Enter и Exit указано, что они должны получать
ссылочный тип object; соответственно тип-значение будет упакован, и ме-
тоду Enter будет передан свой экземпляр упакованного типа-значения, на
который будет поставлена блокировка, а методу Exit – свой экземпляр, на
котором блокировки никогда не было. Понятно, что все остальные потоки
264
CIL и системное программирование в Microsoft .NET
264                         CIL и системное программирование в Microsoft .NET   Разработка параллельных приложений для ОС Windows                     265


         ...                                                                    будут создавать и множить свои собственные упакованные представления
            for ( j = 0; j < m_size; j++ ) {                                    типов-значений, и никакой синхронизации не произойдет. Поэтому при
              for ( k = from; k < to; k++ ) {                                   использовании мониторов важно проследить, чтобы вызовы разных мето-
                Monitor.Enter( m_C );                                           дов в разных потоках использовали один общий объект ссылочного типа.
                try {                                                                Можно выделить интересный момент – типы объектов сами являют-
                  m_C[i,j] += m_A[i,k] * m_B[k,j];                              ся экземплярами класса Type, и для них выделяется место в управляемой
                } finally {                                                     куче. Это позволяет использовать тип объекта в качестве владельца запи-
                  Monitor.Exit( m_C );                                          си SyncBlock:
                }                                                                       ...
              }                                                                            for ( j = 0; j < m_size; j++ ) {
            }                                                                                for ( k = from; k < to; k++ ) {
         ...                                                                                   Monitor.Enter( typeof(double) );
       В этом фрагменте надо выделить два существенных момента: во-пер-                        try {
вых, использование метода Exit в блоке finally, а во-вторых – использо-                          m_C[i,j] += m_A[i,k] * m_B[k,j];
вание всего массива m_C, а не отдельного элемента m_C[i,j].                                    } finally {
       Первое надо взять за правило, так как в случае возникновения ис-                          Monitor.Exit( typeof(double) );
ключения в критической секции блокировка может остаться занятой                                }
(т.е. в случае покидания секции без вызова метода Exit).                                     }
       Второе связано с тем, что элементы m_C[i,j] являются значениями, а                  }
не ссылочными типами. Для типов-значений соответствующее представ-                      ...
ление в управляемой куче не создается, и у них нет и не может быть ссы-              Возможно неявное использование мониторов в C# с помощью клю-
лок на синхронизирующие записи SyncBlock.                                       чевого слова lock:
       Самое плохое в этой ситуации то, что попытка собрать приложение,
использующее типы-значения в качестве аргументов методов Enter и Exit                lock ( obj ) { ... }
(как в примере ниже), пройдет успешно:
         ...                                                                    эквивалентна
            for ( j = 0; j < m_size; j++ ) {
              for ( k = from; k < to; k++ ) {                                        Monitor.Enter( obj ); try { ... }
                Monitor.Enter( m_C[i,j] );                                           finally { Mointor.Exit( obj ); }
                try {
                  m_C[i,j] += m_A[i,k] * m_B[k,j];                                  Использование ключевого слова lock предпочтительно, так как при
                } finally {                                                     этом выполняется дополнительная синтаксическая проверка – попытка
                  Monitor.Exit( m_C[i,j] );                                     использовать для блокировки тип-значение приведет к диагностируемой
                }                                                               компилятором ошибке, вместо трудно отлавливаемой ошибки во время
              }                                                                 исполнения:
            }
         ...                                                                         public static void ThreadProc()
       В прототипах методов Enter и Exit указано, что они должны получать            {
ссылочный тип object; соответственно тип-значение будет упакован, и ме-                int     i,j,k, from, to;
тоду Enter будет передан свой экземпляр упакованного типа-значения, на                 double R;
который будет поставлена блокировка, а методу Exit – свой экземпляр, на                from = (Interlocked.Increment(ref m_stripused) – 1) * m_stripsize;
котором блокировки никогда не было. Понятно, что все остальные потоки                  to = from + m_stripsize;