Реализация критической секции на Interlocked.Exchange
От: SergeyGubanov Россия http://SergeyGubanov.narod.ru/
Дата: 16.06.08 13:06
Оценка:
Реализация критической секции на Interlocked.Exchange

Объясните пожалуйста, а то никак не могу врубиться, почему ежели я реализую критическую секцию используя атомарную операцию обмена, то MSDN рекомендует писать так:
if (System.Threading.Interlocked.Exchange(ref flag, 1) == 0)
{
  // ...
  System.Threading.Interlocked.Exchange(ref flag, 0);
}

а почему бы не написать попроще:
if (System.Threading.Interlocked.Exchange(ref flag, 1) == 0)
{
  // ...
  flag = 0;
}

Второй вариант правильный или нет?

(Кстати, он почти в два раза быстрее работает... )
Re: Реализация критической секции на Interlocked.Exchange
От: Were  
Дата: 16.06.08 13:27
Оценка: -1
Здравствуйте, SergeyGubanov, Вы писали:

SG>Реализация критической секции на Interlocked.Exchange


SG>Второй вариант правильный или нет?


Не правильный. Комбинирование атомарных и неатомарных операций над одной переменной недопустимо. Контекст может переключиться во время выполнения flag = 0;.
Re[2]: Реализация критической секции на Interlocked.Exchange
От: nikov США http://www.linkedin.com/in/nikov
Дата: 16.06.08 13:37
Оценка: +1
Здравствуйте, Were, Вы писали:

W>Не правильный. Комбинирование атомарных и неатомарных операций над одной переменной недопустимо. Контекст может переключиться во время выполнения flag = 0;.


По стандарту C#, присваивания переменным ссылочных и коротких примитивных типов есть атомарная операция:

5.5 Atomicity of variable references
Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic.

Re[2]: Реализация критической секции на Interlocked.Exchange
От: Сергей Юрьевич Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 16.06.08 13:57
Оценка:
Здравствуйте, Were, Вы писали:

W> Контекст может переключиться во время выполнения flag = 0;.


А чему это повредит? Рано или поздно контекст всё-равно вернётся обратно и флаг наконец-то будет сброшен в ноль. Разьве не так?
Re[3]: Реализация критической секции на Interlocked.Exchange
От: Were  
Дата: 16.06.08 14:27
Оценка:
Здравствуйте, nikov, Вы писали:

N>Здравствуйте, Were, Вы писали:


W>>Не правильный. Комбинирование атомарных и неатомарных операций над одной переменной недопустимо. Контекст может переключиться во время выполнения flag = 0;.


N>По стандарту C#, присваивания переменным ссылочных и коротких примитивных типов есть атомарная операция:


Тогда частично беру свои слова назад ) По этому поводу есть неплохая статья:
http://thith.blogspot.com/2005/11/c-interlocked.html
System.Threading.Interlocked предполагает наличие memory barrier, в то время, как инструкция flag = 0; вполне может быть выполнена (благодаря оптимизациям) до того, как отработает код в критической секции.
В любом случае не рекомендую экспериментировать с такими сочетаниями — себе дороже выйдет.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: nikov США http://www.linkedin.com/in/nikov
Дата: 16.06.08 14:46
Оценка:
Здравствуйте, Were, Вы писали:

W>В любом случае не рекомендую экспериментировать с такими сочетаниями — себе дороже выйдет.


Нет, а вот давайте как раз поэкспериментируем
Re[5]: Реализация критической секции на Interlocked.Exchange
От: Were  
Дата: 16.06.08 15:13
Оценка:
Здравствуйте, nikov, Вы писали:

N>Здравствуйте, Were, Вы писали:


W>>В любом случае не рекомендую экспериментировать с такими сочетаниями — себе дороже выйдет.


N>Нет, а вот давайте как раз поэкспериментируем

Смотря какая цель эксперимента? Ради спортивного интереса конечно можно )

Представьте ситуацию: приложение непредсказуемо падает в разных местах. Через неделю отладки на стороне клиента Вы обнаруживаете код:
if (System.Threading.Interlocked.Exchange(ref flag, 1) == 0)
{
  // ...
  // For the best performance!
  flag = 0;
}

После замены последней операции на Interlocked.Exchange, приложение волшебным образом падать перестает.
Интересно, какие части тела Вы захотите оторвать экспериментатору?
Re[6]: Реализация критической секции на Interlocked.Exchange
От: Сергей Юрьевич Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 16.06.08 15:29
Оценка:
Как я понял, грабли могут быть только лишь в излишне глупом оптимизирующем компиляторе, который может по своему усмотрению захотеть переставить местами строчки кода. Не так ли?

Но если я вынесу всё в отдельные процедуры:
private void EnterCriticalSection ()
{
    while (System.Threading.Interlocked.Exchange(ref this.interlockedFlag, 1) != 0)
    {
        System.Threading.Thread.Sleep(0);
    }
}

private void ExitCriticalSection ()
{
    this.interlockedFlag = 0;
}

То неужели глупый оптимизатор чего-то может переставить в следующем коде:
this.EnterCriticalSection();
this.DoSmth();
this.ExitCriticalSection();

?
Re[7]: Реализация критической секции на Interlocked.Exchange
От: Were  
Дата: 16.06.08 16:04
Оценка:
Здравствуйте, Сергей Юрьевич Губанов, Вы писали:

СЮГ>Как я понял, грабли могут быть только лишь в излишне глупом оптимизирующем компиляторе, который может по своему усмотрению захотеть переставить местами строчки кода. Не так ли?


СЮГ>Но если я вынесу всё в отдельные процедуры:


А где гарантия, что он не захочет их заинлайнить? Вообщем что бы Вы не сделали, гарантии 100%-ной работоспособности такого кода Вы врядли получите. А от потенциальных багов лучше избавляться, а не создавать на этапе разработки )
Re[8]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 16.06.08 20:59
Оценка:
Здравствуйте, Were, Вы писали:

W>Здравствуйте, Сергей Юрьевич Губанов, Вы писали:


СЮГ>>Как я понял, грабли могут быть только лишь в излишне глупом оптимизирующем компиляторе, который может по своему усмотрению захотеть переставить местами строчки кода. Не так ли?


СЮГ>>Но если я вынесу всё в отдельные процедуры:


W>А где гарантия, что он не захочет их заинлайнить? Вообщем что бы Вы не сделали, гарантии 100%-ной работоспособности такого кода Вы врядли получите. А от потенциальных багов лучше избавляться, а не создавать на этапе разработки )


Гарантию надо не против инлайнинга искать, а против экономии на записи. А против этого помогает volatile.
The God is real, unless declared integer.
Re[9]: Реализация критической секции на Interlocked.Exchange
От: Were  
Дата: 16.06.08 21:33
Оценка:
Здравствуйте, netch80, Вы писали:

N>Гарантию надо не против инлайнинга искать, а против экономии на записи. А против этого помогает volatile.

Я почему-то считал, что volatile подразумевается, а зря
Re: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 16.06.08 23:46
Оценка: -1 :)
Здравствуйте, SergeyGubanov, Вы писали:

SG>Реализация критической секции на Interlocked.Exchange


SG>Объясните пожалуйста, а то никак не могу врубиться, почему ежели я реализую критическую секцию используя атомарную операцию обмена, то MSDN рекомендует писать так:

SG>
SG>if (System.Threading.Interlocked.Exchange(ref flag, 1) == 0)
SG>{
SG>  // ...
SG>  System.Threading.Interlocked.Exchange(ref flag, 0);
SG>}
SG>

SG>а почему бы не написать попроще:
SG>
SG>if (System.Threading.Interlocked.Exchange(ref flag, 1) == 0)
SG>{
SG>  // ...
SG>  flag = 0;
SG>}
SG>

SG>Второй вариант правильный или нет?

SG>(Кстати, он почти в два раза быстрее работает... )


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

совершенно регулярным способом разделения доступа являеются мьютексы или что-то вроде того. там тред пытающийся залокировать мьютекс — честно ждет в очереди к мьютексу.
все остальное — ерунда.
Re[2]: Реализация критической секции на Interlocked.Exchange
От: nikov США http://www.linkedin.com/in/nikov
Дата: 17.06.08 04:32
Оценка:
Здравствуйте, merk, Вы писали:

M>критическая секция в обычной трактовке, реализуется в ядре оси просто запретом переключать треды до конца секции. диспетчер тредов имеет функцию — запретить/разрешешить переключение.


Это далеко не обычная трактовка. Такое поведение было бы несовместимо с вытесняющей многозадачностью. Критическая секция в обычном понимании отнюдь не мешает переключаться на потоки, не пытающиеся захватить ту же блокировку.
Re[2]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 17.06.08 05:39
Оценка:
Здравствуйте, merk, Вы писали:

M>а где тут критическая секция?

M>критическая секция в обычной трактовке, реализуется в ядре оси просто запретом переключать треды до конца секции. диспетчер тредов имеет функцию — запретить/разрешешить переключение.

In concurrent programming a critical section is a piece of code that accesses a shared resource (data structure or device) that must not be concurrently accessed by more than one thread of execution.

Что то ты не про то говоришь IMHO

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

Чем же в таком случае является WinAPI CRITICAL_SECTION?

M>от есть крит секция опасна и на прикладном уровне, если вы не разрабатываете ядро ос, является признаком бесшабашного программирования.

M>в вашем коде нет никаких указаний, что вы перешли в монопольный режим. вроде. это у вас на чем? типо .net?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.06.08 06:21
Оценка:
Здравствуйте, merk, Вы писали:

M>а где тут критическая секция?

M>критическая секция в обычной трактовке, реализуется в ядре оси просто запретом переключать треды до конца секции. диспетчер тредов имеет функцию — запретить/разрешешить переключение.

Во-первых, мне непонятно, почему для Вас это "обычная" трактовка. Насколько я помню нормы 70-х, там "критической секцией" мог называться, например, кусок кода, выполнять который одновременно могло не более одной задачи. (Вроде synchronized методов в Яве, но мьютекс не на объект, а на функцию.)

Во-вторых, в MS Windows критической секцией называется неименованный видимый только из одного процесса мьютекс (в отличие от того, что по CreateMutex и может быть доступен нескольким процессам). Название действительно слабоадекватное, но обсуждение в данной ветке шло именно в его пределах.

M>совершенно регулярным способом разделения доступа являеются мьютексы или что-то вроде того. там тред пытающийся залокировать мьютекс — честно ждет в очереди к мьютексу.

M>все остальное — ерунда.

Угу. Но пытаясь применить несогласованную терминологию, Вы ушли сильно в сторону.
The God is real, unless declared integer.
Re[10]: Реализация критической секции на Interlocked.Exchang
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.06.08 06:29
Оценка:
Здравствуйте, Were, Вы писали:

W>Здравствуйте, netch80, Вы писали:


N>>Гарантию надо не против инлайнинга искать, а против экономии на записи. А против этого помогает volatile.

W>Я почему-то считал, что volatile подразумевается, а зря :)

Ну, во-первых, его по-любому следовало упомянуть.
Во-вторых, полный рецепт действительно должен сочетать в себе запись 0 в переменную и непосредственно до этого отработку барьера памяти. Потому что иначе может возникнуть ситуация, что процессор переставил действия записи так, что 0 в памяти в ячейке спинлока уже есть (и другие процессоры его видят), но защищаемые спинлоком данные ещё не попали туда. Последствия от чтения устаревших данных могут быть любыми.:(
Желательно также отработать барьер и после этого — чтобы не тянуть с записью нуля в ячейку спинлока, задерживая тем самым других.

А сделали для операции инлайнинг (это на C#-то?) или нет, если она корректно описана — с барьерами — уже неважно.

Большинству программистов вопрос барьеров _пока_ не важен, потому что в x86 форсированная сериализация операций чтения и записи (то есть защищаемые данные гарантированно запишутся в память раньше, чем 0 в спинлок). Но Intel обещает это скоро отключить.

В статье по упомянутой ссылке это объяснено, мне кажется, очень слабо.
The God is real, unless declared integer.
Re: Реализация критической секции на Interlocked.Exchange
От: Сергей Юрьевич Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 17.06.08 07:05
Оценка:
Я тут, вобщем, написал тестовую програмку и запустил её на ночь. Сейчас пришёл на работу, смотрю -- работает. Она всю ночь совершала по 2 миллиона блокировок в секунду и не зависла. Машина двухядерная: Athlon-64 X2. Это конечно ещё ничего не доказывает, но на размышления наводит...

Код:
void EnterCriticalSection ()
{
    while (System.Threading.Interlocked.Exchange(ref this.flag, 1) != 0)
    {
        System.Threading.Thread.Sleep(0);
    }
}

void ExitCriticalSection ()
{
    this.flag = 0;
}
Re[7]: Реализация критической секции на Interlocked.Exchange
От: Erop Россия  
Дата: 17.06.08 07:19
Оценка: +1
Здравствуйте, Сергей Юрьевич Губанов, Вы писали:

СЮГ>То неужели глупый оптимизатор чего-то может переставить в следующем коде:

СЮГ>
СЮГ>this.EnterCriticalSection();
СЮГ>this.DoSmth();
СЮГ>this.ExitCriticalSection();
СЮГ>

СЮГ>?
А ещё умные процы с переупорядочением команд бывают...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: Реализация критической секции на Interlocked.Exchang
От: vdimas Россия  
Дата: 17.06.08 08:13
Оценка:
Здравствуйте, netch80, Вы писали:

N>Во-вторых, полный рецепт действительно должен сочетать в себе запись 0 в переменную и непосредственно до этого отработку барьера памяти. Потому что иначе может возникнуть ситуация, что процессор переставил действия записи так, что 0 в памяти в ячейке спинлока уже есть (и другие процессоры его видят), но защищаемые спинлоком данные ещё не попали туда. Последствия от чтения устаревших данных могут быть любыми.

N>Желательно также отработать барьер и после этого — чтобы не тянуть с записью нуля в ячейку спинлока, задерживая тем самым других.

Вот тут действительно, стоит поэкспериментировать, что быстрее, один Interlocked.Exchange, или пара барьеров памяти. (Хотя второй опциональный, ИМХО, по крайней мере, когда речь идет об однопроцессорной машине).

N>А сделали для операции инлайнинг (это на C#-то?) или нет, если она корректно описана — с барьерами — уже неважно.


N>Большинству программистов вопрос барьеров _пока_ не важен, потому что в x86 форсированная сериализация операций чтения и записи (то есть защищаемые данные гарантированно запишутся в память раньше, чем 0 в спинлок). Но Intel обещает это скоро отключить.


Хм, тогда имеет смысл сделать две реализации, одну только для чтения защищаемых данных (экономим 1 барьер), другую — для чтения и записи. Учитывая, что в большинстве сценариев защищаемые данные пишутся гораздо реже, чем читаются, это может иметь смысл.
Re[11]: Реализация критической секции на Interlocked.Exchang
От: Were  
Дата: 17.06.08 08:46
Оценка:
Здравствуйте, netch80, Вы писали:

N>А сделали для операции инлайнинг (это на C#-то?) или нет, если она корректно описана — с барьерами — уже неважно.


Хм с барьерами-то конечно неважно. Я комментировал код без барьеров )

private void ExitCriticalSection ()
{
    this.interlockedFlag = 0;
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.