Re[4]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.06.08 13:51
Оценка: 13 (2) +1
Здравствуйте, merk, Вы писали:

M>Отвечаю всем троим сразу, что покритиковали мое типо примитивное понимание крит секции.

M>Настоящая СИСТЕМНАЯ крит секция — это то, что я сказал. Реализуется запретом переключений и переходом треда в монопольный режим. Такая крит секция используется для написания ядра и регулярных обьектов синхронизации — мьютексов, семафоров, флагов и проч лабуды. Поскольку их же нужно как-то реализовать???

Спасибо за разъяснение. Но по нему однозначно создаётся впечатление, что Вы учили эту тематику по книгам 20-летней давности (не меньше). А уж утверждение, что есть некоторая "настоящая" критическая секция, а всё остальное, очевидно, ненастоящее — вполне напоминает "я д'Артаньян, а вы все в дерьме". В других местах другие названия, и считать какие-то одни из них настоящими — достаточно смешно.

А теперь про переход в монопольный режим. Во-первых, это наиболее грубый из всех возможных вариантов реализации. Я уже понял, что про wait-free и про lock-free подходы Вы ничего не слышали; тогда почитайте здесь же про них, надеюсь, понравится. Но Вы категорически ограничиваетесь одним процессором! Все Ваши методы типа "запрет переключения" работают только на одном процессоре. Для синхронизации между несколькими процессорами используются другие методы, начиная со спинлоков. В коде, который Вы ругали в следующем письме, приведён именно спинлок. И то, как Вы на него обрушились, свидетельствует именно об устарелости знаний (когда там первые SMP появились?) Особенно это странно сейчас, когда двухъядерный процессор стоит на любом свежем десктопе.

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

M>фишки вроде, это некий псевдокод
M>Critical.enter();
M>...
M>Critical.leave();
M>однозначно транслируются в
M>hidden_mutex.lock();
M>...
M>hidden_mutex.unlock().

Вот в том-то и дело, что не "однозначно", или по крайней мере этот hidden_mutex.lock() делается не как тупой системный вызов. У Sun это названо adaptive mutex, у других — другими именами, но смысл в том, что если interlocked exchange на уровне пользователя даёт захват мьютекса, то этого достаточно, чтобы не просить лишнего от ядра. А по статистике это не менее половины всех захватов (разумеется, в грамотно построенном коде).

В WinAPI, critical section отличается от mutex именно тем, что видима только в одном процессе, но не в другом. Поэтому для неё можно использовать адаптивный захват с ограниченным количеством попыток (а вот если этого недостаточно — оно берёт уже ядерный мьютекс).

M>поскольку базовым обьектом синхронизации является mutex.

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

Это важно. Потому что мьютекс — это не просто семафор с не более чем 1 в счётчике. Это семафор, который умеет, в большинстве реализаций, становиться рекурсивным (то есть один и тот же агент может захватывать его много раз), или с контролем ошибок (повторный захват тем же агентом вызывает генерацию ошибки). Для семафора Дейкстры реализация подобных возможностей слишком дорога и неэффективна. Мьютекс оптимизируется в другом направлении, чем семафор с произвольным счётчиком.

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


Это спинлок. По-английски — spinlock. Читайте книги (хотя бы википедию), там всё сказано.
The God is real, unless declared integer.
Re[4]: итог
От: merk Россия  
Дата: 18.06.08 15:34
Оценка: 6 (1) -2
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, merk, Вы писали:


M>>это не критсекция, это гуано.


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


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

СЮГ>Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.


теория. если время нахождения внутри вашего синхроучастка сравнимо с врмением переключения тредов, внутри участка вас могут переключить. то есть в дело вмешиваются еще доп факторы. если тред хозяин участка переключат, то тред сидящий в полинге задержится в нем на доп время. то есть в дело вступают еще и приоритеты. а если многозадачность вытесняющая..., а высокоприоритетные треды получают все время проца, то вообще все встанет, поскольку высокоприоритетный в полинге будет блокировать низкоприоритеный в участке. бумц. казалось бы правильная фича вообще сломалась!
приехали...
как много оказалось условий, при которых ваш код можно назвать кодом для критсекции.
а?
Re[3]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.06.08 13:36
Оценка: 3 (1) +2
Здравствуйте, merk, Вы писали:

M>это не критсекция, это гуано. хотя и работает как крит секция.

M>у вас тред пытающийся войти в занятую вашу секцию, крутится цикле пока не получит доступа в нее. Бесмысленно тратя ресурсы вашей системы, хотя бы вычислительные.
M>Это же пошло!
M>По настоящему тред не могущий войти в охраняемый участок сходит с диспетчера в очередь ожидающих данного оьекта.

Вместо того, чтобы ругаться грозными словами — Вы бы почитали чего-нибудь по теме:))

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

Во-вторых, есть ситуации, когда переход в спячку под управлением внешнего шедулера просто недопустим. Их мало, но они есть — например, работа того же шедулера, или истинного обработчика аппаратного прерывания.

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


Спасибо за разжёвывание азов, век бы без Вас не догадались ;)

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

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

И дороже.
The God is real, unless declared integer.
Re[4]: и совсем уж про очередь...вспомнилось
От: merk Россия  
Дата: 18.06.08 16:47
Оценка: -2 :)
Здравствуйте, С. Ю. Губанов, Вы писали:
СЮГ>Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.

а что у вас за очередь такая?
безукоризненная очередь должна обеспечивать следующий функционал:
1. число кладущих и число берущих потоков произвольно
2. если очередь пуста — берущий уходит в ожидание(не поллинг!!!) события — очередь стала не пуста. причем с таймаутом, ибо он не может сидеть в ожидании вечно.
3. если очередь переполнилась(бесконечных очередей не бывает) — кладущий уходит в ожидание события "есть место для нового элемента", опять же с таймаутом, по той же причине.
но ваши "секции" защищают только непосредственное заталкивание обьекта в очередь, при условии, что она не переполнена, и взятие, при условии, что не пуста.
если очередь пуста, ваш берущий будет вынужден(в силу вашей реализации) находиться в поллинге. то есть опять двадцать пять, грузим проц, но уже в другом месте.
короче. очереди и стеки, как минимум на таких секциях реализуются или просто некорректно, или активно грузят проц. таймаут не упоминается.
возразите.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: Sergey Россия  
Дата: 17.06.08 13:56
Оценка: 3 (1) +1
С. Ю. Губанов пишет:

> S>Насколько я понимаю, с таким кодом вероятен простой баг — одна нитка еще

> S>не выйдет из критической секции, вторая — уже войдет. Соответственно,
> S>тест должен иметь хорошую чувствительность именно к этой ситуации.
>
> Не понимаю как такое может быть? Ведь пока флаг равен 1 никто не войдёт,
> тут 100% гарантии.

Из-за переупорядочивания инструкций — например, сначала выполнится
последняя flag = 0, потом — предпоследняя что была под секцией. Думаю, в
таком сценарии между ними еще кто-нибудь вклинится может.

> Меня беспокоит другое:

>
> 1) Пусть на одном процессоре пришло время выполнить инструкцию flag = 0;
> 2) а абсолютно одновременно с этим на всех остальных процессорах настало
> время выполнить инструкцию Interlocked.Exchange(ref flag, 1)
>
> так вот, а не может ли такое случится, что ноль флагу не присвоится
> никогда? То есть секция останется заблокированной навечно???

Не вижу, как такое может быть. Хотя, мало ли чего я не вижу.

> Эксперименты показывают, что на протяжении нескольких часов работы

> "сюрпризов" не возникает, т.е. ноль флагу присваивается.

Эксперименты (как и вообще любые тесты) полной гарантии дать не могут,
но для выявления проблем все равно полезны.

> Но вдруг "сюрприза" надо ждать не несколько часов, а месяц или год?


Бывало у меня такое. В одном месте что-нибудь поправишь — не меняя
синхронизацию, вообще посторонний, но исполняющийся параллельно с
интересующим код, в другом месте баг вылезет.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Реализация критической секции на Interlocked.Exchange
От: nikov США http://www.linkedin.com/in/nikov
Дата: 18.06.08 13:15
Оценка: 3 (1) +1
Здравствуйте, merk, Вы писали:

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

M>Это же пошло!
M>По настоящему тред не могущий войти в охраняемый участок сходит с диспетчера в очередь ожидающих данного оьекта.

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

Spinlock
Re[3]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 18.06.08 13:37
Оценка: 3 (1) +1
Здравствуйте, merk, Вы писали:

M>это не критсекция, это гуано. хотя и работает как крит секция.

M>у вас тред пытающийся войти в занятую вашу секцию, крутится цикле пока не получит доступа в нее. Бесмысленно тратя ресурсы вашей системы, хотя бы вычислительные.
M>Это же пошло!
Мсье когда нибудь слышал про SpinLock?

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

M>то есть при нормальном подходе на сленге обчного программиста, если тред в секцию не пускают — он засыпает, пока не получит "сигнал" секция освободилась.
Это классический подход, да. Но в случае если занятая секция занимается на небольшое колво тактов поток напрасно продрыхнет квант времени + потратит время на прогулки в ядро для работы с мутексом. Отсюда появились критические секции со спинлоком. На многоядерных/многопроцессорных конфигурациях это дает некоторое ускорение.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
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[5]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.06.08 14:40
Оценка: +2
Здравствуйте, merk, Вы писали:

N>>Вместо того, чтобы ругаться грозными словами — Вы бы почитали чего-нибудь по теме:))

M>и читал по теме..и писал по теме, могу кинуть код ядра реально работающей RTOS.

Тогда Вы слишком странно пишете.

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

M>Большинство блокировок кратковременные — это вы видимо пишете тем, кто реализует мьютексы со сталкиванием с шедулера???

Переведите, пожалуйста, это предложение. При чём тут "сталкивание с шедулера"? Большинство блокировок типа "захватили мьютекс, поменяли два поля, отпустили мьютекс" действительно кратковременные — значительно короче, чем вход в режим ядра.

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


Конечно, не даю. Конечно, как общее решение неприменимо. Но разве автор того кода говорил, что у него единственный допустимый вариант? Чего это Вы вдруг на него обрушились?

N>>Во-вторых, есть ситуации, когда переход в спячку под управлением внешнего шедулера просто недопустим. Их мало, но они есть — например, работа того же шедулера, или истинного обработчика аппаратного прерывания.

M>не спячку, а ожидание.

В данном случае это ожидание — именно спячка;) потому что спинлока тоже ждут, но иначе.

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

M>что касается вашего опасения, что это мол долго, то реализуется это очень просто
M>время тратится на
M>1. проверка занятости сьютекса(_счетчик >0 && _owner!=CurrentThread)-> перевод обьекта CurrentThread из двухсвязного списка активных тредов шедулера в такой же список ожидающих на этом мьютексе. переключение контекста. ФСЕ!

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

M> самое длинное — переключение контекста. не хотите переключаться, сидите в полинге. иного не дано.


Дано, таки дано. Вы слишком узко рассматриваете область возможных применений.

N>>Спасибо за разжёвывание азов, век бы без Вас не догадались ;)

M>пожалста.

Не делайте вид, что Вы не оценили сарказм. Но Ваше чрезмерное тяготение в подходах к одному, совсем не распространённому, типу ситуации (один процессор, слабое разделение прав), который соответствует Вашим письмам — совсем не отражает типичную для многих других картину. И, обрушиваясь на них только потому, что условия не соответствуют Вашим — делаете хуже только себе.
The God is real, unless declared integer.
Re[3]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 18.06.08 13:46
Оценка: 10 (1)
Здравствуйте, merk, Вы писали:

M>это не критсекция, это гуано.


У меня сейчас есть несколько свободных минут, поэтому я Вам отвечу, хотя Вы этого совершенно не заслуживаете так как Вы чрезвычайно самонадеяны и публично делаете неправильные обобщающие утверждения, а почти ничего не знаете. Вам нужно ещё очень многому научиться.

Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.

M>не нужно пытаться обмануть и превзойти разработчиков ядра оси.


А Вы не допускаете мысли, что я как раз и есть разработчик некоего рантайма? Думаете боги горшки обжигают?
Re: Реализация критической секции на Interlocked.Exchange
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 24.06.08 08:18
Оценка: 9 (1)
Здравствуйте, SergeyGubanov, Вы писали:

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


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


Статья как раз про это:

The main purpose of this is to announce “availability” of the locks to other processors. More specifically, it ensures that before the current processor is able to turn around and reacquire the lock in its own private cache, that other processors at least have the opportunity to see the write.

http://www.smalltalk.ru << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[5]: Реализация критической секции на Interlocked.Exchange
От: remark Россия http://www.1024cores.net/
Дата: 19.06.08 11:04
Оценка: 6 (1)
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, remark, Вы писали:


R>>Кстати, из вышеуказанных 2 утверждений не следует, что flag объявлен как volatile.


СЮГ>В C# целочисленная переменная flag не может быть объявлена как volatile по правилам языка, так как если её объявить как volatile, то её нельзя будет передать по ссылке. А внутрь функций семейства System.Threading.Interlocked.*** изменяемая переменная должна быть передана как раз по ссылке, то есть она не может быть "волатильной". Короче, любой кто предлагает объявить в C# "интерлочный" флаг "волатильным", просто никогда сам не пробовал этого сделать в C#, а думает что это правильно по аналогии с какими-то другими языками программирования, например Си/Си++, но в C# это не так.



Ладно, уговорил, больше не буду отвечать на вопросы по C#.
Напоследок напишу, что надо так (это проверил — компилируется и работает):
int main(array<System::String^>^)
{
    int flag = 0;
    System::Threading::Interlocked::Exchange(flag, 1);
    System::Threading::Thread::VolatileWrite(flag, 0);
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[14]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 07.07.08 20:24
Оценка: 5 (1)
Здравствуйте, netch80, Вы писали:

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


R>>>А откуда дровишки? Можно какие-нибудь ссылки?

R>>>Они ж вроде как только недавно как раз задокументировали свою модель памяти, и там написано, что load имеет семантику acquire, а store — release.

N>>Вероятно, мои данные устарели — они это обещали года три назад.


N>Примерно тогда вводились команды RFENCE, SFENCE, MFENCE.


Аааа... Вроде я слышал что-то такое, что Intel какое-то время назад опубликовал некую модель памяти x86-SPO, и она действительно была более слабая, чем текущая. Но насколько я знаю это дело сейчас заглохло, и никаких процессов, реализующих x86-SPO не было. Вот тут вроде есть упоминание x86-SPO:
http://gee.cs.oswego.edu/dl/jmm/cookbook.html


N>> А что значит "имеет семантику acquire"? И где про это почитать?


N>Уточняю вопрос — чем это отличается по результату (не по определению) от простого упорядочения операций? Описание такого рода:


N>

N>In between those extremes are a lot of different possibilities. Those possibilities are explained in terms of acquire and release semantics:
N> * A normal load or store can be freely reordered with respect to other normal load or store operations.
N> * A load with acquire semantics creates a downwards fence. This means that normal loads and stores can be moved down past the load.acquire, but nothing can be moved to above the load.acquire.
N> * A store with release semantics creates an upwards fence. This means that normal loads and stores can be moved above the store.release, but nothing can be moved to below the store.release.
N> * A full fence is effectively an upwards and downwards fence. Nothing can move in either direction across a full fence.

(источник: http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx)


N>даёт, если мы считаем, что все load автоматически acquire, а store — release, что переупорядочение вообще между ними невозможно


Из этого не следует, что переупорядочивания между ними не возможны. Например, в таком примере:
store_release(x, 1);
store_release(y, 1);

или в таком:
r = load_acquire(x);
store_release(y, 1);

переупорядочивая не возможны. А вот в таком:
store_release(y, 1);
r = load_acquire(x);

возможны. Например, процессор может выполнить (и скорее всего так и сделает) это как:
r = load_acquire(x);
store_release(y, 1);

Заметь, через acquire ничего не прошло вврех, а через release ничего не прошло вниз.

Это, кстати, занятное отличие модели памяти Java от С# (CLI). CLI позволяет делать переупорядочивание, как я показал, а Java — нет.


N>Далее там же видим:


N>

N>In terms of the above, the memory model for X86 can be described as:
N> 1. All stores are actually store.release.
N> 2. All loads are normal loads.
N> 3. Any use of the LOCK prefix (e.g. ‘LOCK CMPXCHG’ or ‘LOCK INC’) creates a full fence.


N>что расходится с Вашим описанием (в пункте 2).


К сожалению сейчас ссылка не открывается, но судя по всему этот материал датирован 2003 годом. Я думаю проблема в этом. До недавнего времени Intel вообще официально не описывал модель памяти x86. Поэтому всё это было в виде "из уст в уста".

Вот официальная модель памяти Intel x86 (датирована 08-2007):
http://www.intel.com/products/processor/manuals/318147.pdf

В качестве общего материала ещё можно посмотреть Memory Ordering in Modern Microprocessors:
http://www.linuxjournal.com/article/8211
http://www.linuxjournal.com/article/8212
(там хорошее введение в вопрос, но датирована она 2005, и там тоже говорится, что x86 может переупорядочивать загрузки)

Ещё есть The JSR-133 Cookbook for Compiler Writers:
http://gee.cs.oswego.edu/dl/jmm/cookbook.html
(это уже 2008 год, и там корректно указана модель памяти x86)


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
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[7]: Реализация критической секции на Interlocked.Exchange
От: Erop Россия  
Дата: 17.06.08 07:19
Оценка: +1
Здравствуйте, Сергей Юрьевич Губанов, Вы писали:

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

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

СЮГ>?
А ещё умные процы с переупорядочением команд бывают...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 12:29
Оценка: -1
Здравствуйте, Сергей Юрьевич Губанов, Вы писали:

СЮГ>Я тут, вобщем, написал тестовую програмку и запустил её на ночь. Сейчас пришёл на работу, смотрю -- работает. Она всю ночь совершала по 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[4]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 18.06.08 13:37
Оценка: +1
Здравствуйте, merk, Вы писали:

M>Настоящая СИСТЕМНАЯ крит секция — это то, что я сказал.

Мсье может подтвердить сказанное ссылкой на авторитетный проверяемый источник?
Потому как пардон, но я не верю на слово никому, особенно тем, кто претендует на знание "как правильно на самом деле", без соответствующих тому доказательств.

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

Мсье не в курсе как работает InterlockedExchange? На фоне заявленного "Большой опыт системного программирования" это выглядит несколько странно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[4]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:27
Оценка: -1
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, merk, Вы писали:


M>>это не критсекция, это гуано.


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


СЮГ>Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.


M>>не нужно пытаться обмануть и превзойти разработчиков ядра оси.


СЮГ>А Вы не допускаете мысли, что я как раз и есть разработчик некоего рантайма? Думаете боги горшки обжигают?


так бы сразу и сказали. тока меня приучили к мысли, что под критсекцией подразумеватся нечто скорее прикладное, что нужно делать регулярным образом, как я сказал. требовать от прикладного софта особых рантайм харакетристик, ну в обычных задачах — не очень правильно.
Я думал вы радостно ушли в поллинг как простой юзер, ожидая обьект...ну например ожидая когда файл прекратят писать. чтобы его почитать. в таком случае сами понимаете счет идет не на такты проца, а на доли секунды, секунды.
Re[5]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:48
Оценка: :)
Здравствуйте, netch80, Вы писали:

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


M>>Отвечаю всем троим сразу, что покритиковали мое типо примитивное понимание крит секции.

M>>Настоящая СИСТЕМНАЯ крит секция — это то, что я сказал. Реализуется запретом переключений и переходом треда в монопольный режим. Такая крит секция используется для написания ядра и регулярных обьектов синхронизации — мьютексов, семафоров, флагов и проч лабуды. Поскольку их же нужно как-то реализовать???

N>Спасибо за разъяснение. Но по нему однозначно создаётся впечатление, что Вы учили эту тематику по книгам 20-летней давности (не меньше). А уж утверждение, что есть некоторая "настоящая" критическая секция, а всё остальное, очевидно, ненастоящее — вполне напоминает "я д'Артаньян, а вы все в дерьме". В других местах другие названия, и считать какие-то одни из них настоящими — достаточно смешно.


N>А теперь про переход в монопольный режим. Во-первых, это наиболее грубый из всех возможных вариантов реализации. Я уже понял, что про wait-free и про lock-free подходы Вы ничего не слышали; тогда почитайте здесь же про них, надеюсь, понравится. Но Вы категорически ограничиваетесь одним процессором! Все Ваши методы типа "запрет переключения" работают только на одном процессоре. Для синхронизации между несколькими процессорами используются другие методы, начиная со спинлоков. В коде, который Вы ругали в следующем письме, приведён именно спинлок. И то, как Вы на него обрушились, свидетельствует именно об устарелости знаний (когда там первые SMP появились?) Особенно это странно сейчас, когда двухъядерный процессор стоит на любом свежем десктопе.


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

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

M>>фишки вроде, это некий псевдокод
M>>Critical.enter();
M>>...
M>>Critical.leave();
M>>однозначно транслируются в
M>>hidden_mutex.lock();
M>>...
M>>hidden_mutex.unlock().

N>Вот в том-то и дело, что не "однозначно", или по крайней мере этот hidden_mutex.lock() делается не как тупой системный вызов. У Sun это названо adaptive mutex, у других — другими именами, но смысл в том, что если interlocked exchange на уровне пользователя даёт захват мьютекса, то этого достаточно, чтобы не просить лишнего от ядра. А по статистике это не менее половины всех захватов (разумеется, в грамотно построенном коде).


ФСЕ! пошел читать про этот могучий exchange. он уже оказывает и мьютексы захватывает... ладно закопали. думал он просто меняет переменные.
Это в .NET? это спинлок? что это ваще?

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

я так и думал и никакой excahge тут не причем. его отсутсвие позволяет решить проблему не менее принципиально.

M>>поскольку базовым обьектом синхронизации является mutex.

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

N>Это важно. Потому что мьютекс — это не просто семафор с не более чем 1 в счётчике. Это семафор, который умеет, в большинстве реализаций, становиться рекурсивным (то есть один и тот же агент может захватывать его много раз), или с контролем ошибок (повторный захват тем же агентом вызывает генерацию ошибки). Для семафора Дейкстры реализация подобных возможностей слишком дорога и неэффективна. Мьютекс оптимизируется в другом направлении, чем семафор с произвольным счётчиком.

а то мы не знали.
Re[6]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 15:02
Оценка: :)
Здравствуйте, netch80, Вы писали:

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


N>>>Вместо того, чтобы ругаться грозными словами — Вы бы почитали чего-нибудь по теме

M>>и читал по теме..и писал по теме, могу кинуть код ядра реально работающей RTOS.

N>Тогда Вы слишком странно пишете.


N>Переведите, пожалуйста, это предложение. При чём тут "сталкивание с шедулера"? Большинство блокировок типа "захватили мьютекс, поменяли два поля, отпустили мьютекс" действительно кратковременные — значительно короче, чем вход в режим ядра.

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



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


N>Конечно, не даю. Конечно, как общее решение неприменимо. Но разве автор того кода говорил, что у него единственный допустимый вариант? Чего это Вы вдруг на него обрушились?


N>>>Во-вторых, есть ситуации, когда переход в спячку под управлением внешнего шедулера просто недопустим. Их мало, но они есть — например, работа того же шедулера, или истинного обработчика аппаратного прерывания.

M>>не спячку, а ожидание.

N>В данном случае это ожидание — именно спячка потому что спинлока тоже ждут, но иначе.


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

M>>что касается вашего опасения, что это мол долго, то реализуется это очень просто
M>>время тратится на
M>>1. проверка занятости сьютекса(_счетчик >0 && _owner!=CurrentThread)-> перевод обьекта CurrentThread из двухсвязного списка активных тредов шедулера в такой же список ожидающих на этом мьютексе. переключение контекста. ФСЕ!

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


косорукая винда — не показатель. ну не хочу я рассматривать win32 как изящный пример концептуальной системной библиотеки.

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

Все! Понял, больше не буду. Большинство выч систем в мире имеют в себе N процессоров, миллионы тредов которые меняют по два поля в каком-то обьекте данных.
Большинство систем в мире носятся на самом деле в кармане, ездят, летают, плавают. И даже не стоят на столе. не говоря уже о мейнфреймах. и этот разрыв будет только увеличиваться.
и наша задача, чтобы это большинство — работало.
Re[6]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 18.06.08 16:42
Оценка: +1
Здравствуйте, merk, Вы писали:

N>>А теперь про переход в монопольный режим. Во-первых, это наиболее грубый из всех возможных вариантов реализации. Я уже понял, что про wait-free и про lock-free подходы Вы ничего не слышали; тогда почитайте здесь же про них, надеюсь, понравится. Но Вы категорически ограничиваетесь одним процессором! Все Ваши методы типа "запрет переключения" работают только на одном процессоре. Для синхронизации между несколькими процессорами используются другие методы, начиная со спинлоков. В коде, который Вы ругали в следующем письме, приведён именно спинлок. И то, как Вы на него обрушились, свидетельствует именно об устарелости знаний (когда там первые SMP появились?) Особенно это странно сейчас, когда двухъядерный процессор стоит на любом свежем десктопе.


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


Должен что? Тут пропущено слово.

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


Ну тогда надо действительно как-то это решать. Например, сказать более конкретно, чего хочется.

N>>Вот в том-то и дело, что не "однозначно", или по крайней мере этот hidden_mutex.lock() делается не как тупой системный вызов. У Sun это названо adaptive mutex, у других — другими именами, но смысл в том, что если interlocked exchange на уровне пользователя даёт захват мьютекса, то этого достаточно, чтобы не просить лишнего от ядра. А по статистике это не менее половины всех захватов (разумеется, в грамотно построенном коде).

M>ФСЕ! пошел читать про этот могучий exchange. он уже оказывает и мьютексы захватывает...
M> ладно закопали. думал он просто меняет переменные.

А какой критерий захвата мьютекса? Вот например классический ядерный мьютекс FreeBSD — в поле владения у него 32-битное значение из 8 битов на номер процессора (0xff — свободен) и 24 бита на рекурсивный счётчик. Захват делается проверкой, что старое значение содержит 0xff в старшем бите, и CAS с этим значением как старым и целевым (свой процессор и 1 в счётчике) как новым. Зачем ещё что-то, если успешный захват сам по себе даёт все необходимые данные? Вот если он не состоялся с N-й попытки, вот тогда уже надо с очередями играться.

M>Это в .NET? это спинлок? что это ваще? :)


А Вы не ёрничайте и не пытайтесь смотреть свысока на "какую-то .NET что это ваще", а подумайте. Это всегда полезно:)

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

M>я так и думал :) и никакой excahge тут не причем. его отсутсвие позволяет решить проблему не менее принципиально.

Ошибаетесь. В многопроцессорном случае, без атомарных операций типа того же CAS (compare and swap), представителем которого является Interlocked.exchange, не обойтись. Даже если Вы успешно заблокировали текущий процессор от переключения на другую задачу и даже запретили все прерывания — другой процессор всё равно сможет влезть. Вот тут-то и приходится использовать атомарные операции — или спинлок на общение с группой мьютексов (можно даже каждому свой), или групповые атомарные обмены, или любой другой вариант — но никак не без него.

И я это Вам в прошлом сообщении писал — но Вы упорно цепляетесь за однопроцессорный случай.

N>>Это важно. Потому что мьютекс — это не просто семафор с не более чем 1 в счётчике. Это семафор, который умеет, в большинстве реализаций, становиться рекурсивным (то есть один и тот же агент может захватывать его много раз), или с контролем ошибок (повторный захват тем же агентом вызывает генерацию ошибки). Для семафора Дейкстры реализация подобных возможностей слишком дорога и неэффективна. Мьютекс оптимизируется в другом направлении, чем семафор с произвольным счётчиком.

M>а то мы не знали. :)

Вы так пишете, что непонятно, чего Вы не знаете, что игнорируете принципиально, а что — ради спора. Поэтому приходится объяснять, пока есть время и настроение. Когда не будет — начну тупо игнорировать — потому что Вы слишком неконструктивны.

N>>Переведите, пожалуйста, это предложение. При чём тут "сталкивание с шедулера"? Большинство блокировок типа "захватили мьютекс, поменяли два поля, отпустили мьютекс" действительно кратковременные — значительно короче, чем вход в режим ядра.

M>Имеется ввиду перемещение из списка активных в список ожидающих событие.

Ах, вот какой у Вас жаргон? Ну так очередь ожидающих вообще-то тоже под управлением шедулера.

M>Что за задачи вы там решаете? когда много тредов меняют два поля??? зачем так много тредов и так мало полей?:)


Ситуации бывают самые разные. Например, добавить в список — типичная задача типа "поменять два поля" (даже если эти поля далеко от самого мьютекса). А списки работ/событий/etc. — для ряда дизайнов и задач являются основным средством.

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


Да, у меня действительно во многом сильносвязанные системы. Например, VoIP софтсвитч. Разные треды делят несколько общих баз, очередей и пулов, на всё это накладывается многонитевый движок событий (очень извращённая штука, но без неё никак).

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


Невозможно — они должны работать раздельно.

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


Как изящный — может, и нет (не хочу сильно спорить о вкусах). Но как контекст _данного_ треда — да. А в данном случае мы к тому же говорим о весьма вменяемой подсистеме.

M>Все! Понял, больше не буду. Большинство выч систем в мире имеют в себе N процессоров, миллионы тредов которые меняют по два поля в каком-то обьекте данных.

M>Большинство систем в мире носятся на самом деле в кармане, ездят, летают, плавают. И даже не стоят на столе. не говоря уже о мейнфреймах. и этот разрыв будет только увеличиваться.

С точки зрения голого количества — да. А вот с точки зрения приложенной работы на них — нет. Как известно, 80-90% всей разработки приходится даже не на продажное ПО, а на внутреннее.

M>и наша задача, чтобы это большинство — работало.


Да пожалуйста, сколько угодно. Я тоже однажды на embedded работал, специфику помню. Но надо же и понимать, что она, мягко говоря, не единственная...
The God is real, unless declared integer.
Re[7]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 17:08
Оценка: -1
Здравствуйте, netch80, Вы писали:

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


N>>>А теперь про переход в монопольный режим. Во-первых, это наиболее грубый из всех возможных вариантов реализации. Я уже понял, что про wait-free и про lock-free подходы Вы ничего не слышали; тогда почитайте здесь же про них, надеюсь, понравится. Но Вы категорически ограничиваетесь одним процессором! Все Ваши методы типа "запрет переключения" работают только на одном процессоре. Для синхронизации между несколькими процессорами используются другие методы, начиная со спинлоков. В коде, который Вы ругали в следующем письме, приведён именно спинлок. И то, как Вы на него обрушились, свидетельствует именно об устарелости знаний (когда там первые SMP появились?) Особенно это странно сейчас, когда двухъядерный процессор стоит на любом свежем десктопе.


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


N>Должен что? Тут пропущено слово.


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

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


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

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

N>Ошибаетесь. В многопроцессорном случае, без атомарных операций типа того же CAS (compare and swap), представителем которого является Interlocked.exchange, не обойтись. Даже если Вы успешно заблокировали текущий процессор от переключения на другую задачу и даже запретили все прерывания — другой процессор всё равно сможет влезть. Вот тут-то и приходится использовать атомарные операции — или спинлок на общение с группой мьютексов (можно даже каждому свой), или групповые атомарные обмены, или любой другой вариант — но никак не без него.


вообще-то разработчики ОС или системного какого-то слоя обязаны давать прикладнику такие способы синхронизации, что корректно работают на целевых платформах. ну чтобы он не мучался с разным кодом для разных конфигураций железа. уж тем более в .NET.

N>И я это Вам в прошлом сообщении писал — но Вы упорно цепляетесь за однопроцессорный случай.


N>>>Это важно. Потому что мьютекс — это не просто семафор с не более чем 1 в счётчике. Это семафор, который умеет, в большинстве реализаций, становиться рекурсивным (то есть один и тот же агент может захватывать его много раз), или с контролем ошибок (повторный захват тем же агентом вызывает генерацию ошибки). Для семафора Дейкстры реализация подобных возможностей слишком дорога и неэффективна. Мьютекс оптимизируется в другом направлении, чем семафор с произвольным счётчиком.


N>>>Переведите, пожалуйста, это предложение. При чём тут "сталкивание с шедулера"? Большинство блокировок типа "захватили мьютекс, поменяли два поля, отпустили мьютекс" действительно кратковременные — значительно короче, чем вход в режим ядра.

M>>Имеется ввиду перемещение из списка активных в список ожидающих событие.

N>Ах, вот какой у Вас жаргон? Ну так очередь ожидающих вообще-то тоже под управлением шедулера.

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

M>>Что за задачи вы там решаете? когда много тредов меняют два поля??? зачем так много тредов и так мало полей?


к тому же в самом низу топика я написал как должна быть реализована очередь(что хотел делать автор). и ваших критсекций просто не достаточно для корректной реализации.
Re[12]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:33
Оценка: +1
Здравствуйте, vdimas, Вы писали:

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


На однопроцессорной машине и Interlocked не нужен.

V>Хм, тогда имеет смысл сделать две реализации, одну только для чтения защищаемых данных (экономим 1 барьер), другую — для чтения и записи. Учитывая, что в большинстве сценариев защищаемые данные пишутся гораздо реже, чем читаются, это может иметь смысл.


При чтении нужны оба барьера. Барьеры — это всегда игра для двух (потоков). Если барьеры выполняет только один поток, то эффекта от них не будет.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Реализация критической секции на Interlocked.Exchange
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:52
Оценка: +1
Здравствуйте, Сергей Юрьевич Губанов, Вы писали:

СЮГ>Я тут, вобщем, написал тестовую програмку и запустил её на ночь. Сейчас пришёл на работу, смотрю -- работает. Она всю ночь совершала по 2 миллиона блокировок в секунду и не зависла. Машина двухядерная: Athlon-64 X2. Это конечно ещё ничего не доказывает, но на размышления наводит...


Это наводит на размышления, что это ничего не доказывает.
Во-первых, не видно объявление flag. Это в данном случае, определяет, корректный это код или нет.
Во-вторых, ловить ошибки алгоритмов синхронизации тестами неимоверно сложно. Составление тестов, которые ловят ошибки это целое искусство. В данном случае, допустим алгоритм некорректный, ну 2 потока одновременно вызвали Sleep(0), ну и что? Возможно это происходило за ночь. Возможно нет.
Программы с подобными ошибками работают *годами* до первого сбоя.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.08 07:30
Оценка: +1
Здравствуйте, merk, Вы писали:

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

N>>Должен что? Тут пропущено слово.
M>иемлось ввиду...ну вот есть программист и ему очень надо. ему обьясняют про синхронизацию — то и се... ну например про крит секцию. он приходит в сюда форум, и видит как типа удачно реализована крит секция в данном топике. типа спинлок. он радостно вставляет это в код...

Вы крайне своеобразно относитесь к другим программистам — судя по Вашим словам, они такие недоумки, что будут хвататься за любой пример кода, не смотря на то, насколько он адекватен задаче, корректен и так далее. Ладно, я бы поверил в такой подход по отношению к индусам (если верить слухам про них). Но на RSDN, мне до сих пор казалось, создано достаточно условий, чтобы так не поступали.

Откуда такое неверие в людей?

N>>Ошибаетесь. В многопроцессорном случае, без атомарных операций типа того же CAS (compare and swap), представителем которого является Interlocked.exchange, не обойтись. Даже если Вы успешно заблокировали текущий процессор от переключения на другую задачу и даже запретили все прерывания — другой процессор всё равно сможет влезть. Вот тут-то и приходится использовать атомарные операции — или спинлок на общение с группой мьютексов (можно даже каждому свой), или групповые атомарные обмены, или любой другой вариант — но никак не без него.

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

Поверьте — такие способы есть. Во множестве.
Но если автор треда захотел сделать именно на атомарном обмене — наверно, у него была какая-то цель? Может, у него задача — сравнить эффективность реализаций (и доказать начальству результаты;)) А может — он курс для студентов пишет.

А Вы тут же обрушились со всей силы на него.

N>>Ах, вот какой у Вас жаргон? Ну так очередь ожидающих вообще-то тоже под управлением шедулера.

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

Где как. На RTOS — может быть. На обычной OS, тред, сидящий в read(), может сидеть так хоть годами — как делают getty на практически любом юниксе.

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


Ну есть и другие варианты. Но действительно так как активизация должна произойти из какого-то кода:) — это или регулярная задача, или обработчик прерывания. А механизмы могут быть разными — например, реализовывать пайп через только мьютексы некорректно, обычно используется что-то вроде condition variable.

M>>>Что за задачи вы там решаете? когда много тредов меняют два поля??? зачем так много тредов и так мало полей?:)

M>к тому же в самом низу топика я написал как должна быть реализована очередь(что хотел делать автор). и ваших критсекций просто не достаточно для корректной реализации.

Я перечитал все сообщения автора в этом треде и не вижу ни одного упоминания очереди. Мне кажется, что Ваше представление про наличие какой-то очереди в его задумке, мягко говоря, поспешно.
The God is real, unless declared integer.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: drol  
Дата: 29.07.08 18:10
Оценка: :)
Здравствуйте, nikov, Вы писали:

СЮГ>>1) В языке C# есть такое правило, что если функция объявлена так: void F (ref int x) то фактический параметр x не может быть volatile.


N>Кстати, Вы не знаете, какое обоснование для этого правила?


Насколько я понимаю, запись в volatile-переменную должна вызывать появление некоторого дополнительного кода непосредственно в районе инструкции записи. Ну там memory barrier какой-нибудь взвести и т.п.
Однако в случае ref неизвестно что за переменная с "той" стороны: обычная или volatile. Вот и задано явное ограничение.

Наверняка это можно было обрулить, но я вижу только пути с существенной потерей производительности покамест...
Реализация критической секции на 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[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[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>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
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[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;
}
Re[12]: Реализация критической секции на Interlocked.Exchang
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 17.06.08 08:57
Оценка:
Здравствуйте, vdimas, Вы писали:

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

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

На однопроцессорной вообще ни один барьер не нужен. А реализация Interlocked.Exchange может и содержать барьеры (тут уже зависит от стиля). Я бы разделил её на две операции — с барьерами и без. С барьерами — удобно для простых случаев.
The God is real, unless declared integer.
Re[2]: Реализация критической секции на Interlocked.Exchange
От: Sergey Россия  
Дата: 17.06.08 12:20
Оценка:
Сергей Юрьевич Губанов пишет:

> Я тут, вобщем, написал тестовую програмку и запустил её на ночь. Сейчас

> пришёл на работу, смотрю -- работает. Она всю ночь совершала по 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;
> }

Насколько я понимаю, с таким кодом вероятен простой баг — одна нитка еще
не выйдет из критической секции, вторая — уже войдет. Соответственно,
тест должен иметь хорошую чувствительность именно к этой ситуации.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 17.06.08 13:13
Оценка:
Здравствуйте, Sergey, Вы писали:

S>Насколько я понимаю, с таким кодом вероятен простой баг — одна нитка еще

S>не выйдет из критической секции, вторая — уже войдет. Соответственно,
S>тест должен иметь хорошую чувствительность именно к этой ситуации.

Не понимаю как такое может быть? Ведь пока флаг равен 1 никто не войдёт, тут 100% гарантии.

Меня беспокоит другое:

1) Пусть на одном процессоре пришло время выполнить инструкцию flag = 0;
2) а абсолютно одновременно с этим на всех остальных процессорах настало время выполнить инструкцию Interlocked.Exchange(ref flag, 1)

так вот, а не может ли такое случится, что ноль флагу не присвоится никогда? То есть секция останется заблокированной навечно??? Эксперименты показывают, что на протяжении нескольких часов работы "сюрпризов" не возникает, т.е. ноль флагу присваивается. Но вдруг "сюрприза" надо ждать не несколько часов, а месяц или год?
Re[3]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 12:17
Оценка:
Здравствуйте, netch80, Вы писали:

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


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

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

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


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


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

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

N>Угу. Но пытаясь применить несогласованную терминологию, Вы ушли сильно в сторону.


Отвечаю всем троим сразу, что покритиковали мое типо примитивное понимание крит секции.
Настоящая СИСТЕМНАЯ крит секция — это то, что я сказал. Реализуется запретом переключений и переходом треда в монопольный режим. Такая крит секция используется для написания ядра и регулярных обьектов синхронизации — мьютексов, семафоров, флагов и проч лабуды. Поскольку их же нужно как-то реализовать???
Пользовательская критсекция, о которой говорите вы, есть скрытый мьютекс, семафор или что-то вроде, что как раз и позволяет одному треду захватить этот скрытый мьютекс, а другому , если он занят, встать в ожидание.
фишки вроде, это некий псевдокод
Critical.enter();
...
Critical.leave();
однозначно транслируются в
hidden_mutex.lock();
...
hidden_mutex.unlock().
поскольку базовым обьектом синхронизации является mutex.
Предупреждаю. я использую mutex как термин. Это обьект через который можно осуществить исключительный доступ к участку кода. В некоторых системах его называют семафором, или еще чем. это неважно.
В приведенном автором коде просто написан некий кусок, где непрерываемым образом какая-то переменная взводится. причем тут крит секция, если в ней нет никакого мьютекса или еще чего. на чем встанет в ожидание тред конкурент? или код просто не дописан?
Re[4]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:18
Оценка:
Здравствуйте, netch80, Вы писали:

N>Вместо того, чтобы ругаться грозными словами — Вы бы почитали чего-нибудь по теме

и читал по теме..и писал по теме, могу кинуть код ядра реально работающей RTOS.

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

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

N>Во-вторых, есть ситуации, когда переход в спячку под управлением внешнего шедулера просто недопустим. Их мало, но они есть — например, работа того же шедулера, или истинного обработчика аппаратного прерывания.

не спячку, а ожидание. общепринято, что тред переходит в режим wait. ожидание события. коим и является освобождение обьекта синхронизации.
что касается вашего опасения, что это мол долго, то реализуется это очень просто
время тратится на
1. проверка занятости сьютекса(_счетчик >0 && _owner!=CurrentThread)-> перевод обьекта CurrentThread из двухсвязного списка активных тредов шедулера в такой же список ожидающих на этом мьютексе. переключение контекста. ФСЕ! самое длинное — переключение контекста. не хотите переключаться, сидите в полинге. иного не дано. но боюсь в поллинге в рельности вы будете сидеть дольше, чем этот не слишком длинный код. Особняком стоят задачи особой рантаймовости, вроде читки с портов ввода, если там скорость поступления данных сравнима со скоростью их обработки процом. особый случай. можно делать и поллинг.

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


N>Спасибо за разжёвывание азов, век бы без Вас не догадались

пожалста.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:21
Оценка:
Здравствуйте, CreatorCray, Вы писали:

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


M>>это не критсекция, это гуано. хотя и работает как крит секция.

M>>у вас тред пытающийся войти в занятую вашу секцию, крутится цикле пока не получит доступа в нее. Бесмысленно тратя ресурсы вашей системы, хотя бы вычислительные.
M>>Это же пошло!
CC>Мсье когда нибудь слышал про SpinLock?

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

M>>то есть при нормальном подходе на сленге обчного программиста, если тред в секцию не пускают — он засыпает, пока не получит "сигнал" секция освободилась.
CC>Это классический подход, да. Но в случае если занятая секция занимается на небольшое колво тактов поток напрасно продрыхнет квант времени + потратит время на прогулки в ядро для работы с мутексом. Отсюда появились критические секции со спинлоком. На многоядерных/многопроцессорных конфигурациях это дает некоторое ускорение.

ежли имелась в виду критсекция со спинлоком, то так и надо в стартовом посте писать. Я б назвал этот метод синхронизации — синхронизацией с особым цинизмом. иногда работает. часто очень плохо.
поскольку никто не оговаривает время коотрое тред будет находиться в вашей критсекции(покажите документацию), то намекать что это время мало — несерьезно. ну то есть ваще несерьезно.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:23
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, merk, Вы писали:


M>>это не критсекция, это гуано.


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


СЮГ>Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.


M>>не нужно пытаться обмануть и превзойти разработчиков ядра оси.


СЮГ>А Вы не допускаете мысли, что я как раз и есть разработчик некоего рантайма? Думаете боги горшки обжигают?
Re[4]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:23
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, merk, Вы писали:


M>>это не критсекция, это гуано.


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


СЮГ>Так вот, описанная мной критическая секция используется в случае когда нужно синхронизировать доступ к очень быстро выполняющемуся участку кода. Быстро — это порядка 20 тактов процессора. Примерно столько же тактов нужно для Interlocked.Exchange на Athlon 64 X2. Пример такого быстрого кода: положить/взять чего-то в очередь, в стек или какой-то другой контейнер. Наибольший выигрыш получается когда количество потоков пытающихся сделать это одновременно равно нескольким сотням. Предлагаю Вам сравнить скорость обычного дотнетного lock () с моей критической секцией в Linux Mono хотя бы на одной сотне потоков конкурирующих за вход в неё. Вы будете приятно удивлены огромаднейшей разницей в скорости работы.


M>>не нужно пытаться обмануть и превзойти разработчиков ядра оси.


СЮГ>А Вы не допускаете мысли, что я как раз и есть разработчик некоего рантайма? Думаете боги горшки обжигают?
Re[5]: Реализация критической секции на Interlocked.Exchange
От: merk Россия  
Дата: 18.06.08 14:34
Оценка:
Здравствуйте, CreatorCray, Вы писали:

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


M>>Настоящая СИСТЕМНАЯ крит секция — это то, что я сказал.

CC>Мсье может подтвердить сказанное ссылкой на авторитетный проверяемый источник?
CC>Потому как пардон, но я не верю на слово никому, особенно тем, кто претендует на знание "как правильно на самом деле", без соответствующих тому доказательств.

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

CC>Мсье не в курсе как работает InterlockedExchange? На фоне заявленного "Большой опыт системного программирования" это выглядит несколько странно.

не в курсе. какое отношение к программированию имеет конкретная функция из конкретного пакета?
скажите как работает — узнаю.
я думал, по старинке, что она просто обеспечивает непрерываемый обмен двух переменных.
ну..иногда надо, в особо тяжелых случаях.
случай реально — особо тяжелый?
Re[6]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 18.06.08 14:53
Оценка:
Здравствуйте, merk, Вы писали:

M>я думал, по старинке, что она просто обеспечивает непрерываемый обмен двух переменных.

Именно. Осмысливать суть ее работы следует в контексте двух и более одновременно работающих ядер/процов.

M>ну..иногда надо, в особо тяжелых случаях.

Отнюдь. Интенсивно курим доки по Lock-free алгоритмам
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[11]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:20
Оценка:
Здравствуйте, netch80, Вы писали:

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



А откуда дровишки? Можно какие-нибудь ссылки?
Они ж вроде как только недавно как раз задокументировали свою модель памяти, и там написано, что load имеет семантику acquire, а store — release.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[12]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:35
Оценка:
Здравствуйте, Were, Вы писали:

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


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


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


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


Есть здесь барьер или нет — нельзя сказать. Это зависит от определения переменной interlockedFlag. Если она volatile, то необходимый барьер здесь есть, и это вполне законный cli код.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:38
Оценка:
Здравствуйте, netch80, Вы писали:

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


Барьеры никак не влияют на задержку. Если бы было какое-то средство для ускорения работы с памятью, поверь мне, оно было бы просто включено всегда по-умолчанию.
Хотя барьер после освобождения критической секции тоже иногда ставится. Но это уже имеет отношение к fairness, и вообще очень спорный подход для общего случая.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Реализация критической секции на Interlocked.Exchange
От: remark Россия http://www.1024cores.net/
Дата: 18.06.08 21:47
Оценка:
Здравствуйте, SergeyGubanov, Вы писали:

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


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


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


Если flag объявлен как volatile, то да, если нет — то нет.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 18.06.08 22:42
Оценка:
Здравствуйте, netch80, Вы писали:

N>CAS (compare and swap), представителем которого является Interlocked.exchange

Небольшая поправка: CAS это InterlockedCompareExchange
Простой InterlockedExchange это для чутка более простых операций.

... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[8]: Реализация критической секции на Interlocked.Exchange
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.08 06:03
Оценка:
Здравствуйте, CreatorCray, Вы писали:

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


N>>CAS (compare and swap), представителем которого является Interlocked.exchange

CC>Небольшая поправка: CAS это InterlockedCompareExchange
CC>Простой InterlockedExchange это для чутка более простых операций.

Да, верно, я их слегка путаю в виндовых названиях.
The God is real, unless declared integer.
Re[12]: Реализация критической секции на Interlocked.Exchang
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.08 06:08
Оценка:
Здравствуйте, remark, Вы писали:

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


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


R>Барьеры никак не влияют на задержку. Если бы было какое-то средство для ускорения работы с памятью, поверь мне, оно было бы просто включено всегда по-умолчанию.


Это ты за слово "тянуть" зацепился?;)) Я имел в виду, чтобы запись нуля в ячейку не была раньше, чем запись тех данных, которые должны стать всем видимы до того, как лок освободится.

R>Хотя барьер после освобождения критической секции тоже иногда ставится. Но это уже имеет отношение к fairness, и вообще очень спорный подход для общего случая.


Угу, потому и только "желательно".
The God is real, unless declared integer.
Re[13]: Реализация критической секции на Interlocked.Exchang
От: vdimas Россия  
Дата: 19.06.08 08:52
Оценка:
Здравствуйте, remark, Вы писали:


R>При чтении нужны оба барьера. Барьеры — это всегда игра для двух (потоков). Если барьеры выполняет только один поток, то эффекта от них не будет.


Ну дык, пишущий делает барьер до и после записи, а все читающие — только перед чтением, я про экономию одного барьера говорил.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 09:01
Оценка:
Здравствуйте, remark, Вы писали:

R>Если flag объявлен как volatile, то да, если нет — то нет.


А Вы вообще как, знаете что такое язык C#? И что означает слово volatile в этом языке? Вы его с Си или с Си++ не перепутали случайно? Пожалуй, перепутали.

Напоминаю:

1) В языке C# есть такое правило, что если функция объявлена так: void F (ref int x) то фактический параметр x не может быть volatile.

2) int System.Threading.Interlocked.Exchange(ref int location1, int value)
Re[5]: итог
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 09:03
Оценка:
Здравствуйте, merk, Вы писали:

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


Большое спасибо, это очень важно, а я об этом позабыл. Надо будет принять меры.
Re[5]: и совсем уж про очередь...вспомнилось
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 09:09
Оценка:
Здравствуйте, merk, Вы писали:

M> 2. если очередь пуста — берущий уходит в ожидание


У меня он уходит на ожидание другим способом, не имеющим к этой критической секции никакого отношения.

А импульсы Monitor.Pulse я посылать не могу, у меня возможно и 16 миллионов сообщений в секунду. Импульсы же эффективны при сотне сообщений в секунду, но не при миллионах...
Re[3]: Реализация критической секции на Interlocked.Exchange
От: nikov США http://www.linkedin.com/in/nikov
Дата: 19.06.08 09:51
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>1) В языке C# есть такое правило, что если функция объявлена так: void F (ref int x) то фактический параметр x не может быть volatile.


Кстати, Вы не знаете, какое обоснование для этого правила?
Re[3]: Реализация критической секции на Interlocked.Exchange
От: remark Россия http://www.1024cores.net/
Дата: 19.06.08 10:13
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>Здравствуйте, remark, Вы писали:


R>>Если flag объявлен как volatile, то да, если нет — то нет.


СЮГ>А Вы вообще как, знаете что такое язык C#? И что означает слово volatile в этом языке? Вы его с Си или с Си++ не перепутали случайно? Пожалуй, перепутали.


СЮГ>Напоминаю:


СЮГ>1) В языке C# есть такое правило, что если функция объявлена так: void F (ref int x) то фактический параметр x не может быть volatile.


СЮГ>2) int System.Threading.Interlocked.Exchange(ref int location1, int value)



Нет, не перепутал.
ECMA-335 12.6.7

A volatile write has “release semantics” meaning that the write is guaranteed to happen after any memory references prior to the write instruction in the CIL instruction sequence.


Кстати, из вышеуказанных 2 утверждений не следует, что flag объявлен как volatile. Но если всё-таки объявлен, то замечательно, пример рабочий.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[14]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 19.06.08 10:15
Оценка:
Здравствуйте, vdimas, Вы писали:

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



R>>При чтении нужны оба барьера. Барьеры — это всегда игра для двух (потоков). Если барьеры выполняет только один поток, то эффекта от них не будет.


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


Сколькими барьерами в данном случае синхронизируется передача владения над защищенными данными при выходе читающего потока и входе пишущего?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[13]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 19.06.08 10:18
Оценка:
Здравствуйте, netch80, Вы писали:

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


R>>Барьеры никак не влияют на задержку. Если бы было какое-то средство для ускорения работы с памятью, поверь мне, оно было бы просто включено всегда по-умолчанию.


N>Это ты за слово "тянуть" зацепился?) Я имел в виду, чтобы запись нуля в ячейку не была раньше, чем запись тех данных, которые должны стать всем видимы до того, как лок освободится.


Барьер после записи тут никак не поможет. Release-барьер всегда делается перед синхронизируещей записью (или вместе, если архитектура совмещает обращения к памяти с барьерами), но не после.

R>>Хотя барьер после освобождения критической секции тоже иногда ставится. Но это уже имеет отношение к fairness, и вообще очень спорный подход для общего случая.


N>Угу, потому и только "желательно".


Моё личное мнение, он там как раз *не* желателен, хотя поставить можно.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 10:24
Оценка:
Здравствуйте, remark, Вы писали:

R>Кстати, из вышеуказанных 2 утверждений не следует, что flag объявлен как volatile.


В C# целочисленная переменная flag не может быть объявлена как volatile по правилам языка, так как если её объявить как volatile, то её нельзя будет передать по ссылке. А внутрь функций семейства System.Threading.Interlocked.*** изменяемая переменная должна быть передана как раз по ссылке, то есть она не может быть "волатильной". Короче, любой кто предлагает объявить в C# "интерлочный" флаг "волатильным", просто никогда сам не пробовал этого сделать в C#, а думает что это правильно по аналогии с какими-то другими языками программирования, например Си/Си++, но в C# это не так.
Re[4]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 10:26
Оценка:
Здравствуйте, nikov, Вы писали:

N>Кстати, Вы не знаете, какое обоснование для этого правила?


Не знаю.
Re[5]: Реализация критической секции на Interlocked.Exchange
От: CreatorCray  
Дата: 19.06.08 10:28
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>если её объявить как volatile, то её нельзя будет передать по ссылке. А внутрь функций семейства System.Threading.Interlocked.*** изменяемая переменная должна быть передана как раз по ссылке, то есть она не может быть "волатильной".

Кстати и правда интересно — а почему запретили то передачу volatile по ссылке?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[5]: Реализация критической секции на Interlocked.Exchange
От: Sergey Россия  
Дата: 19.06.08 10:31
Оценка:
С. Ю. Губанов пишет:

> В C# целочисленная переменная flag не может быть объявлена как volatile

> по правилам языка, так как если её объявить как volatile, то её нельзя
> будет передать по ссылке. А внутрь функций семейства
> System.Threading.Interlocked.*** изменяемая переменная должна быть
> передана как раз по ссылке, то есть она не может быть "волатильной".
> Короче, любой кто предлагает объявить в C# "интерлочный" флаг
> "волатильным", просто никогда сам не пробовал этого сделать в C#, а
> думает что это правильно по аналогии с какими-то другими языками
> программирования, например Си/Си++, но в C# это не так.

А кто пишет что "думает что это правильно по аналогии с какими-то
другими языками программирования, например Си/Си++", не знает С++
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[6]: Реализация критической секции на Interlocked.Exchange
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 12:08
Оценка:
Здравствуйте, remark, Вы писали:

R>Напоследок напишу, что надо так (это проверил — компилируется и работает):

R>
R>int main(array<System::String^>^)
R>{
R>    int flag = 0;
R>    System::Threading::Interlocked::Exchange(flag, 1);
R>    System::Threading::Thread::VolatileWrite(flag, 0);
R>}
R>


Спасибо за ещё один способ. Я его не знал.

Сейчас проверил скорость работы своей системы (которую я в общих чертах описал там: http://www.rsdn.ru/forum/message/2993169.1.aspx
Автор: С. Ю. Губанов
Дата: 19.06.08
) в зависимости от завершителя критической секции доступа к очереди задач.

Первое место занимает самый тупой завершитель:
flag = 0;
16.8 миллионов простых задач в секунду.

На втором месте завершитель:
System.Threading.Interlocked.Exchange(ref flag, 0);
15.5 миллионов простых задач в секунду.

И, наконец, на третьем месте завершитель:
System.Threading.Thread.VolatileWrite(ref flag, 0);
12.5 миллионов простых задач в секунду.

То есть Thread.VolatileWrite по непонятным мне причинам ощутимо медленее чем Interlocked.Exchange.
Re[6]: итог
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 12:34
Оценка:
СЮГ> Большое спасибо, это очень важно, а я об этом позабыл. Надо будет принять меры.

Наверное так сойдёт:
            public static void Enter ()
            {
                int t = 0;
                while (System.Threading.Interlocked.Exchange(ref state, 1) != 0)
                {
                    System.Threading.Thread.Sleep(t / 10);
                    t++;
                }
            }
Re[7]: итог
От: С. Ю. Губанов Россия http://SergeyGubanov.narod.ru/
Дата: 19.06.08 12:38
Оценка:
СЮГ>Наверное так сойдёт:

Чуть модернизировал:
            public static void Enter ()
            {
                int t = 0;
                while (System.Threading.Interlocked.Exchange(ref state, 1) != 0)
                {
                    System.Threading.Thread.Sleep(t++ >> 4);
                }
            }
Re[15]: Реализация критической секции на Interlocked.Exchang
От: vdimas Россия  
Дата: 24.06.08 07:39
Оценка:
Здравствуйте, remark, Вы писали:

R>Сколькими барьерами в данном случае синхронизируется передача владения над защищенными данными при выходе читающего потока и входе пишущего?


Одним, в случае передачи от читателя к писателю, и двумя при передаче от писателя к читателю.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[12]: Реализация критической секции на Interlocked.Exchang
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 27.06.08 08:16
Оценка:
Здравствуйте, remark, Вы писали:

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


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


R>А откуда дровишки? Можно какие-нибудь ссылки?

R>Они ж вроде как только недавно как раз задокументировали свою модель памяти, и там написано, что load имеет семантику acquire, а store — release.

Вероятно, мои данные устарели — они это обещали года три назад. А что значит "имеет семантику acquire"? И где про это почитать?
The God is real, unless declared integer.
Re[13]: Реализация критической секции на Interlocked.Exchang
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 27.06.08 08:28
Оценка:
Здравствуйте, netch80, Вы писали:

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


R>>А откуда дровишки? Можно какие-нибудь ссылки?

R>>Они ж вроде как только недавно как раз задокументировали свою модель памяти, и там написано, что load имеет семантику acquire, а store — release.

N>Вероятно, мои данные устарели — они это обещали года три назад.


Примерно тогда вводились команды RFENCE, SFENCE, MFENCE.

N> А что значит "имеет семантику acquire"? И где про это почитать?


Уточняю вопрос — чем это отличается по результату (не по определению) от простого упорядочения операций? Описание такого рода:

In between those extremes are a lot of different possibilities. Those possibilities are explained in terms of acquire and release semantics:



* A normal load or store can be freely reordered with respect to other normal load or store operations.
* A load with acquire semantics creates a downwards fence. This means that normal loads and stores can be moved down past the load.acquire, but nothing can be moved to above the load.acquire.
* A store with release semantics creates an upwards fence. This means that normal loads and stores can be moved above the store.release, but nothing can be moved to below the store.release.
* A full fence is effectively an upwards and downwards fence. Nothing can move in either direction across a full fence.

(источник: http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx)

даёт, если мы считаем, что все load автоматически acquire, а store — release, что переупорядочение вообще между ними невозможно (или последствия не уточнены, потому что везде говорится про normal loads and stores).

Далее там же видим:

In terms of the above, the memory model for X86 can be described as:



1. All stores are actually store.release.
2. All loads are normal loads.
3. Any use of the LOCK prefix (e.g. ‘LOCK CMPXCHG’ or ‘LOCK INC’) creates a full fence.


что расходится с Вашим описанием (в пункте 2).
The God is real, unless declared integer.
Re[8]: итог
От: merk Россия  
Дата: 30.06.08 00:22
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

СЮГ>>Наверное так сойдёт:


СЮГ>Чуть модернизировал:

СЮГ>
СЮГ>            public static void Enter ()
СЮГ>            {
СЮГ>                int t = 0;
СЮГ>                while (System.Threading.Interlocked.Exchange(ref state, 1) != 0)
СЮГ>                {
СЮГ>                    System.Threading.Thread.Sleep(t++ >> 4);
СЮГ>                }
СЮГ>            }
СЮГ>


для скорости не слип нужно делать, а yield — переключение тредов.
не знаю точно, но похоже в .NET это sleep(0). смотрел по диагонали, yield там вроде нет.
но семантика sleep(0) как раз нужная.
Re[16]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 07.07.08 20:00
Оценка:
Здравствуйте, vdimas, Вы писали:

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


R>>Сколькими барьерами в данном случае синхронизируется передача владения над защищенными данными при выходе читающего потока и входе пишущего?


V>Одним, в случае передачи от читателя к писателю, и двумя при передаче от писателя к читателю.


В случае передачи от читателя к писателю — проблема, т.к. одним барьером нельзя сделать синхронизацию. Т.е. грубо говоря, при передаче владения от читателя к писателю выполнение читателей и писателя "наложится во времени" с непредсказуемыми результатами.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[17]: Реализация критической секции на Interlocked.Exchang
От: vdimas Россия  
Дата: 10.07.08 17:41
Оценка:
Здравствуйте, remark, Вы писали:


R>В случае передачи от читателя к писателю — проблема, т.к. одним барьером нельзя сделать синхронизацию. Т.е. грубо говоря, при передаче владения от читателя к писателю выполнение читателей и писателя "наложится во времени" с непредсказуемыми результатами.


Там было не про передачу управления, а о доступе к атомарной ячейке памяти (в которой указатель, например). Писателю нужно сделать 2 барьера — до и после модификации этой ячейки, читателю — только до. Если в момент чтения читателем в многопроцессорной системе писатель тоже можифицирует ячейку — фиг с ним, в близком времени эта модификация видна будет только после барьера этого писателя.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[18]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 10.07.08 17:52
Оценка:
Здравствуйте, vdimas, Вы писали:

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



R>>В случае передачи от читателя к писателю — проблема, т.к. одним барьером нельзя сделать синхронизацию. Т.е. грубо говоря, при передаче владения от читателя к писателю выполнение читателей и писателя "наложится во времени" с непредсказуемыми результатами.


V>Там было не про передачу управления, а о доступе к атомарной ячейке памяти (в которой указатель, например). Писателю нужно сделать 2 барьера — до и после модификации этой ячейки, читателю — только до. Если в момент чтения читателем в многопроцессорной системе писатель тоже можифицирует ячейку — фиг с ним, в близком времени эта модификация видна будет только после барьера этого писателя.


Почему *только* после барьера писателя? По моим представлениям модификация может быть видна и до барьера, выполненного писателем... если, конечно, мы не говорим о non cache-coherent системах...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[19]: Реализация критической секции на Interlocked.Exchang
От: vdimas Россия  
Дата: 14.07.08 19:27
Оценка:
Здравствуйте, remark, Вы писали:


R>Почему *только* после барьера писателя? По моим представлениям модификация может быть видна и до барьера, выполненного писателем... если, конечно, мы не говорим о non cache-coherent системах...


Не суть, будет ли она видна до барьера. Барьер, при доступе к атомарной ячейке после записи "ускоряет" обнаружение изменений у читателей.

R>
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[20]: Реализация критической секции на Interlocked.Exchang
От: remark Россия http://www.1024cores.net/
Дата: 14.07.08 19:50
Оценка:
Здравствуйте, vdimas, Вы писали:

R>>Почему *только* после барьера писателя? По моим представлениям модификация может быть видна и до барьера, выполненного писателем... если, конечно, мы не говорим о non cache-coherent системах...


V>Не суть, будет ли она видна до барьера.


Ну как же не суть! Это как раз основное, что обеспечивает мьютекс. Если нас устраивает ситуация, что читатели читают произвольную помесь из разных версий данных, то нам мьютекс и вообще не нужен.

V>Барьер, при доступе к атомарной ячейке после записи "ускоряет" обнаружение изменений у читателей.





1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Реализация критической секции на Interlocked.Exchange
От: remark Россия http://www.1024cores.net/
Дата: 20.08.08 08:18
Оценка:
Здравствуйте, С. Ю. Губанов, Вы писали:

R>>Если flag объявлен как volatile, то да, если нет — то нет.


СЮГ>А Вы вообще как, знаете что такое язык C#? И что означает слово volatile в этом языке? Вы его с Си или с Си++ не перепутали случайно? Пожалуй, перепутали.


СЮГ>Напоминаю:


СЮГ>1) В языке C# есть такое правило, что если функция объявлена так: void F (ref int x) то фактический параметр x не может быть volatile.



Читаю блог Joe Duffy:
http://www.bluebytesoftware.com/blog/CommentView,guid,1665653b-b5f3-49b4-8144-cfbc5e8c632b.aspx

Тут он делает именно это — переменную, объявленную как volatile, передаёт в Interlocked.Exchange(). Единственное на это выдаётся варнинг, который в данном случае просто false positive. А так всё работает.
Возможно он использует более позднюю версию языка и компилятора. Но тем не менее теперь это можно делать.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.