Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 100
Текст из файла (страница 100)
СогкрагеЕхспапг?е, который применяется в потоке отчетау Вспомните, что поскольку множество потоков пытаются выполнить запись в переменную тпгеаг)Соплы поток отчета должен читать ее значение также в синхронизированном режиме. И здесь на помощь приходит 1птег1оскег).СотрагеЕхсяапгте. Метод 1пкег1оскег!.Согврагекхспапсе, как и можно предположить из его имени. позволяет обменивать значение одной переменной, если текущее значение эквивалентно сравниваемому значению другой переменной в атомарном режиме, н возвращает значение, которое было сохранено ранее в этом месте. Поскольку в классе 1птег1оскег) не предусмотрен метод для простого чтения значения 1пт32 в атомарной операции, этот обмен значения переменной ппгяЬегтпгеаг)з выполняется с ее собственным значением, а в качестве побочного эффекта метод 1птег1оскес.
СоглрагеЕхспапсе возвращает значение, которое было в элементе. Методы 1пкех1ос)кегЗ на ЗМР-системах На симметричных многопроцессорных системах )п1е! (ЗМР) простое чтение и запись элементов памяти встроенного размера синхронизируются автоматически, В системе )А-32 чтение и запись свойств, выровненных по 32-битным значениям, синхронизированы, Поэтому в предыдущем примере, где было показано применение 1пкег1оскег). СоглрагеЕхспапсе для простого чтения значения 1пс32, вызов этого метода был бы не обязательным, если переменная правильно выровнена в памяти.
По умолчанию С)Я выполняет немало действий, чтобы правильно выровнять значения по естественным границам. Однако можно переопределить размещение значений в классе или структуре, применив к полям атрибут е1е1г)011зеснссг1Ьпсе, тем самым отменяя выравнивание полей данных, Если значение 1пс32 не выровнено, то гарантии, упомянутые в предыдущем абзаце, будут утеряны.
В таком случае для надежного чтения значения должен применяться метод 1пгег1оскег).СоирагеЕхспапое. Все методы 1псег1оскег)... реализованы в системах )А-32 с использованием префикса 1осх. данный префикс заставляет процессор применять сигнал РОскз, а это предотвращает параллельный доступ к значению других процессоров системы, что и требуется в сложных операциях, когда увеличиваются значения счетчиков и тому подобное.
Одно удобное качество префикса 1оск заключается в том, что не выровненные поля данных не повлияют пагубным образом на целостность блокировки. Другими словами, он отлично работает с не выровненными данными. Вот почему 1псег1осхес) . соглрагеехсьапсе — гарантия атомарного чтения не выровненных данных. И, наконец, примите во внимание тот факт, что в классе 1псег1оскег) реализованы перегрузки некоторых методов, позволяющие им работать с 64-битными значениями, числами с плавающей точкой и ссылками на объекты. В действительности в 1псег1осхег)...
даже предлагаются обобщенные перегрузки для работы со ссылками на объекты любых типов. Рассмотрим, что означает атомарно работать с 64-битными значениями на 32-разрядных системах. Естественно, не существует способа атомарно читать такие значения без обращения к классу 1пкег1асхео, Фактически по этой причине в версии .)чеТ 2.0 класса 1псег1осхег) предусмотрен метод 1псег1оскег) .ееаг) для значений типа 1пс64. конечно, такой метод не нужен на 64-разрядных системах, где он сводится к обычному чтению. Однако среда СЫ предназначена для работы на многих платформах, поэтому при манипуляциях с 64-битными значениями всегда следует применять 1псег1осхег).
ееаг). По этим причинам лучше перестраховаться, чем потом сожалеть, и воспользоваться 1псег1оскег) . СоглрагеЕхспапг?е Для чтЕния и записи значЕНий В атОмарнОМ РЕжИмЕ, пОСкОЛьку ОчЕнь труднО проверить факт наличия или отсутствия выравнивания или превышения системного размера атомарных данных, прежде чем читать или писать их в "сыром" виде. Определение системного размера атомарной единицы данных для платформы и построение кода на основе этой информации совершенно противоречит межплатформенному духу управляемого кода. 378 Глава (2 И последний момент, на который следует обратить внимание — ключевое слово то1аг11е в рассмотренном примере.
Обращаясь к переменным а свободной от бпокировок манере и полагаясь на автоматическую синхронизацию процессора а многопоточной среде, необходимо снабжать такие переменные ключевым словом чо1аг11е. В противном случае компилятор может внести различные отклонения а процессе оптимизации кода, которые могут изменить логику кода. Ключевое слово чо1аг11е помимо прочего препятствует такого рода оптимизации, и гарантирует, что код в полной мере будет выполняться именно е том порядке, как он написан.
когда методы класса 1псег1оскас( используются со значениями, помеченными чо1аг11е, компилятор выдаст предупреждение С80420, начинающееся со слов "а ге(егепсе (о а чо(аб(е бе1б нй! по( Ье (геа(еб аз чо(аб)е" (ссылка на поле то1аг11е не будет трактоваться как то1аг11е), Если аы прочтете описание этого предупреждения а документации МВОМ, то увидите, что одним из исключений из правила является вызов методов 1пгег1оскег). Поэтому а данном случае можно успешно подавить выдачу этих предупреждений с помощью директивы Зргадиа иагптпд, В дополнение предположим, что имеется деа 32-битных целых, представляющих дее блокировки по имени 1оск1 и 1оск2.
пусть также система требует, чтобы 1оск1 устанавливалась перед 1осх2. Код может быть написан, исходя из этого, но во время выполнения процессор найдет причину изменить порядок операций из соображений эффективности, потому что он предположит, что эти дае блокировки совершенно независимы друг от друга, и что установка их е обратном порядке более эффективна. Для решения этой проблемы потребуется поместить вызов тьгеао.метогуваггЬег между обращениями к этим двум переменным.
Метод ТЬгеао) . Меногуваггтег обеспечит сохранение заданного порядка. Последний метод класса 1 и Г е г 1 о ох е г(, который следует рассмотреть— СогарагеЕхспапде. Как уже было показано, этот небольшой метод действительно удобен. Он похож на 1пге г1ос кег) . сотра геехсьапде в том отношении, что позволяет передать значение из места положения или элемента в атомарном режиме. Однако он выполняет такую передачу, только если первоначальное значение при сравнении оказывается эквивалентным предоставленному образцу, и все эти операпни выполняются совместно и атомарно.
В любом случае метод всегда возвращает первоначальное значение. Одним из чрезвычайно удобных применений метода СоирагеЕхспапде является создание легковесной спин-блокировки (ар!п 1ос(г). Особенность спин-блокировки в том. что если она не может захватить блокировку, то запускает маленький цикл, ожидая появления такой возможности. Обычно при реализации спин-блокировки поток переводится в спящий режим на очень краткий период времени прн каждой неудачной попытке захватить блокировку.
Таким образом. планировщик потоков получает возможность предоставить время для выполнения другому потоку, пока происходит ожидание. Если поток не нужно переводить в спящий режим, а только освободить его текущий квант времени, то методу Тпгеаг(. Я1еер можно передать значение О. Ниже показан пример: оятпд Буясеиг оя1пд Яуясеи.10) иятпд Яуясев.ТЬгеа61пд; роЬ11о с1аяя МуЯр1оЬоох ( роЬ11о МузртпЬоох( Тпс яр1пиа1Г ) ( сная.яртпиатс = ярапиазсг ) роЬ11о тото Епсег() ( нЬ11е( 1поег1осхеО.СоирагеЕхопапде(гет ГЬеьосх, 1, О) == 1 ) ( УУ Блокировка занята, ожидать. Тпгеаг).Б1еер( ярзпиато )) рпЪЬЬс чосс) Ех1С () ( г'г' Сбросить блокировку. тпСег1ос)тес.сощрагеЕхспапде( гег Спеьосс, 0 )г ) ргсчаге чо1аг11е 1пг Сйеьос)г = 0; рггчапе гпС зр).пиа).ст ) роЪ11с с1азв МуЯрупЬоссмапаЯег: 101ярояаЪ1е ( рпЪЬЬс Музр1пьос)сиапаоег( МузрупЬосс зрупЬосй ) ( СЪ1я.яр1пьос)< = врьпЬос)с) яргпЬосН.ЕпСег()т ) рпЪ11с чо1б 01зрозе() ( вр1пЬосн.Ехгг()г ) рггчасе муБрупьосс вр1пьос)сг ) рпвугс с1азя ЕпггуРогпС ( ягаггс рггчате Еапбощ гпд = пен Капбощ()г рг1чаге зпаСЬс Музрспьос)с 1ооьос)т = пен МуЯргпьосн( 10 ) рггчаСе вгаС1с 5Сгеащигггег ТяьоО = пен БСгеащкгЬСег( Р11е.ореп("1од.гхг", Рг1еМобе.йррепб, Рг1ейссеяв.кггСе, Р11езпаге.ноле) ); рггчаге вгаС1с чо1б ЕпбТЪгеабропс() ( пвгпд( пен Музр1пьос)тиападег(1одьос)с) ) ( Тзьод.кг1СеЬЬпе( "Поток запускается" )) ГзьоО.Р1сзп()г ) гпС Сгше = гпб.нехС( 10, 200 )г тъгеаб.я1еер( саше )т пяспд( пен Му5ртпЬоснмапацег(1одьосй) ) ( хвьоЯ.игусеь1пе( "поток завершается" )т Гзьод.Р1овп()г ) згаггс чогб Ма1п() ( /Г Запустить потоки, ожидающие в течение случайного периода времени.
Тйгеас[] гпбСПгеабя = пен Тпгеаб[ 50 ); Тот( пспс 1 = Ог г < 50т е+г ) ( гпбгпгеабя[1] пен Тпгеаб( пен Тпгеас)ЯСагг( Епггуро1пс.кпбтпгеаСГппс) )г гпссъгеабя(1].Ясагс()г Этот пример подобен предыдущему. В нем создается 50 потоков, которые ожидают в течение случайного периода времени. Однако вместо управления счетчиком потоков он выводит строку в журнальный файл, Поскольку эта запись осуществляется из множества потоков, и методы экземпляра ЯСгеащнг1Сег не являются безопасными в от- 380 Глава (2 ношении потоков, запись должна выполняться в безопасной манере внутри контекста блокировки. И здесь на помощь приходит класс Муэрэп1.осх. Внутренне он управляется переменной блокировки в форме целочисленного значения и использует 1псег1оскес(.
СоярагеЕхснапде для регулировки доступа к блокировке. Вызов тпгег1осхес(.СотрагеЕхснапдев Музр1пЬосК.Епкег говорит следующее. 1. Если значение блокировки равно О, заменить его значением 1, чтобы обозначить установку блокировки; в противном случае не делать ничего. 2. Если значение элемента уже содержит 1, значит, он занят, и нужно перевести поток в спящее состояние и ожидать. Оба эти действия происходят в атомарном режиме через класс 1пкег1осхещ так что нет никакой возможности, чтобы более одного потока в единицу времени захватило блокировку. Когда вызывается метод Муэр1пЕоск. Ех1щ то все, что ему нужно сделать— это сбросить блокировку.
Однако это также должно быть сделано атомарно — отсюда и вызов 1пгег1осхег(. СоярагеЕхснапде. на заметку! поскольку внутренне блокировка представлена типом Епс (который является 1пс32), можно просто установить нулевое значение в муБР1пьоск. ехэс, Однако, как упоминалось в предыдущей врезке, следует проявлять осторожность с блокировкой 64-битного значения при работе на 32-разрядной машине, Что если инженер поддержки изменит лежащее в основе хранилище с 1пс на 1псРсг (тип, указывающий размер указателя, который зависит от платформы) и не внесет изменения в месте, где выполняется сброс спеьоск? В данном примере иллюстрируется применение идиомы "б!вроэаЫе/пв!пд" для реализации детерминированной деструкции, когда вводится другой класс, в данном случае Муэрэпьосхмападег, чтобы реализовать идиому КАП. Этот избавляет от необходимости повсеместного написания блоков г1па11у.
Конечно, все равно приходится помнить о применении ключевого слова из1пд,но если следовать этой идиоме более тщательно, чем в данном примере. то понадобится реализовать фннализатор, который выдаст предупреждение в отладочной сборке, если объект не будет надлежащим образом освобожден'. Имейте в виду, что спин-блокировки, реализованные подобным образом, не реентерабельны.















