Re[7]: ConcurrentDictionary vs reference type
От: karbofos42 Россия  
Дата: 19.02.22 07:54
Оценка: 3 (1) +2 -1 :))
Здравствуйте, Codealot, Вы писали:

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


S>>Очень многие нашли возможным использовать эту коллекцию. Сам ей пользуюсь. Про то, что ее реализация (одна из) использует барьеры памяти узнал только что. До этого не был уверен и это ничего не меняло в возможности мной использовать эту коллекцию. Может быть я что-то делаю не так?


C>Да, ты всё делаешь совершенно не так. Ты пишешь многопоточный код и не понимаешь, как он работает и корректен ли вообще. Сегодня он работает, а потом звезды встанут неправильно и всё взорвется к черту.


S>>В каком смысле нельзя использовать объекты? Почему нельзя? Кто это сказал?


C>Ну ты сам сказал что если ничего не написано про барьер памяти, то полагаться на это нельзя. А если полагаться на него нельзя, то использовать там объекты тоже нельзя. Потому что read/write reordering и прочие хтонические ужасы.


На msdn к этому классу есть документация и поведение всех методов расписано достаточно подробно.
Например, метод AddOrUpdate гарантирует, что в результате будет в словаре уникальный ключ, но не гарантирует, что переданный делегат на добавление или изменение значения вызовется один раз.
Допустим, 10 потоков вызвали этот метод одновременно для одного и того же ключа. Все 10 потоков увидят, что такого элемента в словаре ещё нет и каждый создаст свой объект для добавления в словарь.
В словарь же добавится только один из этих объектов и все 10 потоков получат в качестве результата один и тот же объект (который попал в словарь, а не который в этом потоке был создан).
9 потоков выполнят ненужную работу и создадут ненужные объекты. Это поведение расписано и именно его нужно учитывать, зачем тут в реализации разбираться, если словарь ведёт себя так, как описано в документации?
Я думал, что документацию для того и делают, чтобы людям не приходилось копаться в реализации и выяснять как что работает.
Если документацию не читать, а использовать классы по наитию, то тут уже конечно нельзя ни на что полагаться
Re: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 22:22
Оценка: +6
Здравствуйте, Codealot, Вы писали:

C>ConcurrentDictionary гарантирует безопасность reference types, или нужны какие-то дополнительные движения с бубном? Документация чего-то не рассматривает этот вопрос

Все, что гарантирует ConcurrentDictionary, написано в его документации и его гарантии никак не распространяются на все остальное. Если в его значениях ссылки, то словарь гарантирует что ссылка будет приписана к значению. А каким образом там пользователь словаря работает с тем, на что ссылка ссылается — не словаря дело.
Re[6]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 23:27
Оценка: -6
Здравствуйте, samius, Вы писали:

S>Очень многие нашли возможным использовать эту коллекцию. Сам ей пользуюсь. Про то, что ее реализация (одна из) использует барьеры памяти узнал только что. До этого не был уверен и это ничего не меняло в возможности мной использовать эту коллекцию. Может быть я что-то делаю не так?


Да, ты всё делаешь совершенно не так. Ты пишешь многопоточный код и не понимаешь, как он работает и корректен ли вообще. Сегодня он работает, а потом звезды встанут неправильно и всё взорвется к черту.

S>В каком смысле нельзя использовать объекты? Почему нельзя? Кто это сказал?


Ну ты сам сказал что если ничего не написано про барьер памяти, то полагаться на это нельзя. А если полагаться на него нельзя, то использовать там объекты тоже нельзя. Потому что read/write reordering и прочие хтонические ужасы.
Ад пуст, все бесы здесь.
Re: ConcurrentDictionary vs reference type
От: gusilebedi  
Дата: 21.02.22 00:58
Оценка: +5 :)
Здравствуйте, Codealot, Вы писали:

C>ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only), или нужны какие-то дополнительные движения с бубном?

C>Документация чего-то не рассматривает этот вопрос

Речь об этом?

var o = new MyObject() { A = "Hello World", B = 42 };
_dic.TryAdd("MyKey", o);
//в другом потоке видим такой ужас, потому что объект в коллекцию добавился "раньше" чем произошла инициализация
Debug.Assert(_dict["MyKey"].A == null);

Ответ таков — .NET Memory Model гарантирует, что все будет ок, то есть такого порядка выполнения быть не может
var o = new MyObject();
_dic.TryAdd("MyKey", o);
o.A = "Hello World"; 
o.B = 42;


PS: ваш стиль общения не способствует получению ответов, следующий раз вы рискуете остаться в твердой уверенности, что разработчики .NET идиоты, а вы нашли дыру в реализации.
Re[51]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 15.03.22 06:54
Оценка: 116 (3) -1 :)
Здравствуйте, Codealot, Вы писали:

X>>Для .net, который компилируется в CIL и только потом CLR-ом в нейтив — нет. Т.к. CLR добавляет барьеры для платформ там где требуется.


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


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

X>>Тогда уточни вопрос из первого сообщения темы: что за опасность подстерегает объекты, когда они хранятся в ConcurrentDictionary?

X>>А прям идеально было бы кусок кода, который ведет себя некорректно при использовании ConcurrentDictionary.
C>Повторю еще раз, для самых-самых чукчеписателей.
Т.е. кода не будет. Можешь дальше не продолжать.

Выглядит все так, будто откопал древний-предревний баян, про который все давно знают, а то и успели забыть, а тебе явилось откровение.
Я понимаю когда люди спрашивают у коллеги и/или на форуме вместо того что бы погуглить ответ, что бы сэкономить свое время за счет чужого.
Но тебе же даже ответы не нужны.

C>Если там есть гарантированные барьеры памяти (в том числе и при извлечении), то никаких проблем быть не должно.

О! Прогрес. Фаза отрицания сменилась на фазу сомнения.

C>... использовать CD таким образом — некорректно.

Каким "таким" образом? Ты же не привел не единой строчки кода с использованием CD

з.ы. итог для тех кому интересен итог:
краткий список правил clr memory model:

Rule 1: Data dependence among loads and stores is never violated.

Rule 2: All stores have release semantics, i.e. no load or store may move after one.

Rule 3: All volatile loads are acquire, i.e. no load or store may move before one.

Rule 4: No loads and stores may ever cross a full-barrier (e.g. Thread.MemoryBarrier, lock acquire, Interlocked.Exchange, Interlocked.CompareExchange, etc.).

Rule 5: Loads and stores to the heap may never be introduced.

Rule 6: Loads and stores may only be deleted when coalescing adjacent loads and stores from/to the same location.


в зависимости от аппаратной платформы CLR генерирует соотвествуюущие инструкции процессора для соблюдения вышеуказанных правил.

для типового использования:
globalAccessibleVar = new SomeClass(...); // thread 1
var f = globalAccessibleVar.someField; // any other thread

Rule 2 не позволяет записать поле объекта после ссылки на объект и Rule 1 не позволяет прочитать поле объекта до чтения сслки на объект.

потенциально можно отдать ссылку на недокоструированный объект из конструктора, и тогда появится возможность прочитать нули из неинициализированных полей.
Но тут я процитирую коментарий от Eric Lippert-а: https://stackoverflow.com/questions/51180784/can-memory-reordering-cause-c-sharp-to-access-unallocated-memory

EL> the CLR guarantees you that its own invariants will be preserved.

EL> But the CLR is not in the business of preserving your invariants!
Отредактировано 15.03.2022 14:35 xpalex . Предыдущая версия .
Re[13]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 23.02.22 04:09
Оценка: 48 (4) :)
Здравствуйте, Codealot, Вы писали:

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


S>> Такого не может быть ибо за ссылку и объект отвечает GC. Есть WeakReference но сам то WeakReference существует.


C>Ты явно не понимаешь, как это всё работает. В том треде, который создает объект — да, там всё есть. В других тредах может быть видно практически любое случайное частичное подмножество из этих данных. Абсолютно никаких гарантий по этому вопросу .NET не дает.


Т.е. до спеки ты не добрался?

https://www.ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf

I.12.6.8 Other memory model issues
All memory allocated for static variables (other than those assigned RVAs within a PE file, see
Partition II) and objects shall be zeroed before they are made visible to any user code.
A conforming implementation of the CLI shall ensure that, even in a multi-threaded environment
and without proper user synchronization, objects are allocated in a manner that prevents
unauthorized memory access and prevents invalid operations from occurring. In particular, on
multiprocessor memory systems where explicit synchronization is required to ensure that all
relevant data structures are visible (for example, vtable pointers) the Execution Engine shall be
responsible for either enforcing this synchronization automatically or for converting errors due to
lack of synchronization into non-fatal, non-corrupting, user-visible exceptions.
It is explicitly not a requirement that a conforming implementation of the CLI guarantee that all
state updates performed within a constructor be uniformly visible before the constructor
completes. CIL generators can ensure this requirement themselves by inserting appropriate calls
to the memory barrier or volatile write instructions.

Re[18]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.02.22 06:29
Оценка: 10 (1) +3 :)
Здравствуйте, Serginio1, Вы писали:

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


C>>А теперь вопрос на миллион: почему ConcurrentDictionary позволяет работать с объектами, если на самом деле этот юз кейс некорректен?

S> Еще раз напомни в чем проблема. Я так и не понял.
S>Все используют и вдруг появился Codealot и выбрасывает в утиль.
S>Поделись. Тут все кроме тебя не понимают! Объясни на пальцах как должно быть!


Он опасается использовать объекты в многопоточности без бубна в виде дополнительного барьера, "Потому что read/write reordering и прочие хтонические ужасы.."
https://rsdn.org/forum/dotnet/8198545.1
Автор: Codealot
Дата: 19.02.22


S>Наверное мы тупые

тупые, но в хорошей компании

"Поместил на одном ядре, извлек на другом. Что там и в каком порядке и когда пишется/читается — это такая загадка, от которой свихнулся бы даже сам Хокинг."
https://rsdn.org/forum/dotnet/8198548.1
Автор: Codealot
Дата: 19.02.22


"У Parallel.ForEach тоже про это ничего не написано. Что наводит на мысль, что документацию просто писали кретины."
https://rsdn.org/forum/dotnet/8198521.1
Автор: Codealot
Дата: 19.02.22


Т.е. указания о том, почему так делать можно или нельзя, он почему-то пытается найти в документации библиотечных типов, а не в модели памяти. И на том основании, что он не находит упоминание в каждом библиотечном типе, предназначенном для многопоточности, называет разработчиков кретинами. И, видимо, об остальных пользователях дотнета такого же мнения.
Re[5]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 23:35
Оценка: +5
Здравствуйте, Codealot, Вы писали:

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


S>>Я не понимаю, почему нельзя использовать объект от того, что я его куда-то поместил и потом извлек? С объектом ничего не происходит в момент добавления. В чем проблема его использовать?


C>Поместил на одном ядре, извлек на другом. Что там и в каком порядке и когда пишется/читается — это такая загадка, от которой свихнулся бы даже сам Хокинг.


Никакой загадки нет, словарь гарантирует корректный доступ к своему содержимому. Учитывая то, что содержимое — это ассоциации ключ-значение и только лишь. В отношении объектов, которые лежат по ссылкам в значениях — никаких гарантий нет.
Re[11]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 22.02.22 10:04
Оценка: +4 :)
Здравствуйте, Codealot, Вы писали:
C>Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.
Это как так? Архитектура дотнета не даёт возможности получить ссылку без объекта.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.02.22 10:07
Оценка: +2 :)))
Здравствуйте, Codealot, Вы писали:

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


S>>Да, нужно.


C>Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.

Тут не ConcurrentDictionary надо запрещать, а сами объекты. Даже GC не сможет переварить такие звезды.
Re[20]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 21.02.22 17:24
Оценка: 6 (1) +2 :)
Здравствуйте, Codealot, Вы писали:

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


S>>Т.е. указания о том, почему так делать можно или нельзя, он почему-то пытается найти в документации библиотечных типов, а не в модели памяти.


C>Объясняю еще раз, для самых сообразительных. Полагаться на модель памяти можно только в том случае, если ConcurrentDictionary гарантированно использует барьер памяти.

Вот тут ошибка. Модель памяти — это часть стандарта CLI, на котором построен .NET и C# в частности. И если мы не можем полагаться на модель памяти, то речь не о .NET, C#, а о какой-то поделке. И с этой поделкой в другой форум. Какой именно — не знаю. Может, в философию...
В этом форуме мы обсуждаем ситуации, когда Execution Engine и JIT компиляторы делают все, что бы на модель памяти можно было полагаться. Из этого и исходим далее.


C>Ну а ты сам же писал, что полагаться на это нельзя, раз про это не написано в документации. Так что — либо использовать объекты в CD нельзя, либо документацию все же писали кретины.


Любой способ передачи ссылки на объект, расположенный в куче .NET обеспечивает видимость корректно инициализированного объекта. За это отвечает реализация EE/JIT. Вплоть до размещения барьера памяти внутри конструктора, при необходимости.

S>>И, видимо, об остальных пользователях дотнета такого же мнения.


C>Не забудь добавить, что как мы выяснили, ты ничего не знал про memory read-write reordering, но все равно писал многопоточный код.

Я многого не знаю, и, возможно, не узнаю. Особенно того, что за пределами абстракций и гарантий, которые я использую для работы.
Re[7]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 00:01
Оценка: +4
Здравствуйте, Codealot, Вы писали:

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


S>>В отношении объектов, которые лежат по ссылкам в значениях — никаких гарантий нет.


C>Тогда такой словарь попросту нельзя использовать с объектами и их использование нужно запретить. Чего не наблюдается.

Мыло-мочало, начинай сначала
Re[18]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 24.02.22 22:38
Оценка: -2 :))
Здравствуйте, Sinclair, Вы писали:

Ты подчистил вопрос. Где объяснения, почему это должно быть неважно для данного случая?

S>Конкретный пример будет?


Пример чего, по твоему, должен быть? Сначала конкретный вопрос, потом ответ.
Ад пуст, все бесы здесь.
Re[33]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 28.02.22 05:41
Оценка: +4
Здравствуйте, Codealot, Вы писали:

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


S>> Ну ты хоть пример то приведи! А то все болтовня!


C>Я не виноват, что до тебя не доходит.

C>Вот например почитай https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Ну ты и некропост откопал. jdk5.

Для того что бы прочитать поле недоинициализированного объекта нужно что бы переупорядочилось store, store

thread 1:
// 1. выделение места на куче
load tmp, (result of allocation)
// 2. работа конструктора
store fieldValue, [tmp + offset]
// 3. возврат инициализированного объекта
store tmp, [objectRefVariable]

thread 2:
// 1. получение ссылки на объект
load [objectRefVariable]
// 2. получение ссылки/значения поля объекта.
load [objectRefVariable + offset]

если процессор переупорядочит шаг 2 и шаг 3 в thread 1, то thread 2 может прочитать неинициализированное поле.

Но если открыть описание memory model clr 2.0, то там явно указано, что reordering STORE,STORE запрещен. Т.е. рантайм берет на себя отвественность по генерации соотвествующего нативного кода при jit-е

Поэтому ты сначала сделай proof-of-concept, который сможет прочитать неиницилизировнное поле сконструированного объекта, а потом уже можешь пугать всех страшными багами в CLR.

А пока все выглядит так, что ты первый раз увидел, что DoubleCheckedLocking не работает (этой инфе лет 15 уже).
Re[12]: ConcurrentDictionary vs reference type
От: vsb Казахстан  
Дата: 19.02.22 17:43
Оценка: 4 (1) +2
Здравствуйте, samius, Вы писали:

vsb>>Реордеринг памяти это когда в my_x будет 0, а в my_y будет 2, хотя казалось бы логичны лишь варианты (0, 0) если до другого потока не дошли изменения; (1, 0) если дошло лишь первое изменение и (1, 2) если дошли оба изменения.


S>Понятно, это на уровне конкретного CPU, и в модели памяти дотнета (и джавы тоже) такого нет.


В модели памяти Java есть конкретные гарантии. И реализация JVM их обеспечивает на любом CPU, котором она работает. В частности чтобы вышеописанного не было, и используются инструкции для барьеров памяти.

Думаю, в .NET примерно то же должно бть.

vsb>>На x86 этой ситуации быть не может. На некоторых других архитектурах вроде ARM — может быть.

S>Такое — верятно. Но речь ведь в данном треде немного о другом, почему можно взять из ConcurrentDictionary ссылку на объект и ей пользоваться... Но ответ как раз в модели памяти дотнета, а не в библиотечном классе ConcurrentDictionary или его документации.

Я согласен, даже если в ConcurrentDictionary что-то не дописали, надо просто предполагать, что он работает так, как от него ожидается.
Re[4]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 22:54
Оценка: :)))
Здравствуйте, samius, Вы писали:

S>Это детали реализации, а не документации.


Это очень важная деталь реализации, которая легко может поставить крест на возможности использовать эту коллекцию.

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


У Parallel.ForEach тоже про это ничего не написано. Что наводит на мысль, что документацию просто писали кретины. Если с ними нельзя использовать объекты — то про это надо бы написать большими красными буквами, не?
Ад пуст, все бесы здесь.
Re[7]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 23:32
Оценка: +2 -1
Здравствуйте, Codealot, Вы писали:

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


S>>Очень многие нашли возможным использовать эту коллекцию. Сам ей пользуюсь. Про то, что ее реализация (одна из) использует барьеры памяти узнал только что. До этого не был уверен и это ничего не меняло в возможности мной использовать эту коллекцию. Может быть я что-то делаю не так?


C>Да, ты всё делаешь совершенно не так. Ты пишешь многопоточный код и не понимаешь, как он работает и корректен ли вообще. Сегодня он работает, а потом звезды встанут неправильно и всё взорвется к черту.

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

S>>В каком смысле нельзя использовать объекты? Почему нельзя? Кто это сказал?


C>Ну ты сам сказал что если ничего не написано про барьер памяти, то полагаться на это нельзя. А если полагаться на него нельзя, то использовать там объекты тоже нельзя. Потому что read/write reordering и прочие хтонические ужасы.


Какое отношение имеет реордеринг в реализации словаря к объектам, которые я в него добавляю по ссылке?
Re[6]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 23:58
Оценка: :)))
Здравствуйте, samius, Вы писали:

S>В отношении объектов, которые лежат по ссылкам в значениях — никаких гарантий нет.


Тогда такой словарь попросту нельзя использовать с объектами и их использование нужно запретить. Чего не наблюдается.
Ад пуст, все бесы здесь.
Re[13]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 16:48
Оценка: +2 :)
Здравствуйте, Codealot, Вы писали:

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


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


C>Нет, не давай уберем. Мы говорим о типе, созданном специально для многопоточности. И если простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. Будешь спорить?

Лет несколько назад — поспорил бы.

А сейчас — ты сам себе Буратино!
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 17:22
Оценка: -3
Здравствуйте, samius, Вы писали:

S>Лет несколько назад — поспорил бы.

S>А сейчас — ты сам себе Буратино!

То есть ты уже понял что облажался, но признать этот факт не в состоянии.
Ад пуст, все бесы здесь.
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 17:24
Оценка: -3
Здравствуйте, Serginio1, Вы писали:

S> А что по твоему еще должно быть?


Повторю и для тебя тоже.
Мы говорим о типе, созданном специально для многопоточности. И если в нем простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. А это значит, что generic constraint типа должен в явном виде их запретить.
Ад пуст, все бесы здесь.
Re[3]: ConcurrentDictionary vs reference type
От: gusilebedi  
Дата: 08.03.22 23:20
Оценка: +1 -1 :)
Здравствуйте, Codealot, Вы писали:

C>Где написано, что она это гарантирует? Потому что я ничего подобного в ней не видел.


С превеликим удовольствием посылаю вас в man и RTFM, лучшего ответа вы не заслужили.
Re[5]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 23:14
Оценка: +2
Здравствуйте, Codealot, Вы писали:

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


C>Это очень важная деталь реализации, которая легко может поставить крест на возможности использовать эту коллекцию.

Очень многие нашли возможным использовать эту коллекцию. Сам ей пользуюсь. Про то, что ее реализация (одна из) использует барьеры памяти узнал только что. До этого не был уверен и это ничего не меняло в возможности мной использовать эту коллекцию. Может быть я что-то делаю не так?

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


C>У Parallel.ForEach тоже про это ничего не написано. Что наводит на мысль, что документацию просто писали кретины. Если с ними нельзя использовать объекты — то про это надо бы написать большими красными буквами, не?

В каком смысле нельзя использовать объекты? Почему нельзя? Кто это сказал?
Re[9]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 00:02
Оценка: +1 :)
Здравствуйте, Codealot, Вы писали:

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


C>Я говорю о реордеринге памяти. Ты вообще понял, о чем идет речь?

Про реордеринг инструкций — слышал. Про барьеры памяти — тоже. Реордерниг памяти — о чем ты?
Re[7]: ConcurrentDictionary vs reference type
От: Teolog  
Дата: 19.02.22 11:27
Оценка: +1 :)
C>Тогда такой словарь попросту нельзя использовать с объектами и их использование нужно запретить. Чего не наблюдается.

Словарь хранит ссылки и позволяет их добавлять и удалять из разных потоков без самопальной внешней синхронизации. Это все для чего он предназначен, и все что делает.
Синхронизация доступа в содержимому объекта по ссылке-отдельный веселый вопрос, который к словарю никакого отношения не имеет, и должен решаться вне зависимости от существования, не-существования, реализации и наличия документации на словарь, тем кто делал класс объекта.
Re[8]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 15:15
Оценка: :))
Здравствуйте, Teolog, Вы писали:

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

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

Если словарь дает какие-то гарантии по поводу барьера памяти, то это один вопрос, и содержимое объекта можно использовать когда ты его получил из словаря.
Если он таких гарантий не дает, то получив ссылку на объект в другом треде, использовать эту ссылку ты не можешь. А это значит, что сохранять ссылки в словарь попросту нельзя. А это значит, что ссылки на объекты надо запретить в generic constraints класса. Доходит?
Ад пуст, все бесы здесь.
Re[9]: ConcurrentDictionary vs reference type
От: Teolog  
Дата: 19.02.22 18:04
Оценка: +1 :)
Какого к чертям барьера?
Какие операции он должен разделять и зачем?
Причем тут синхронизация доступа к содержимому объекта?

Вам с вашим вопросом не сюда, а в туториал -как работает многопоточность, что такое атомарность, для чего нужна каждая разновидность объектов синхронизации.
Re[16]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 19:30
Оценка: :))
Здравствуйте, karbofos42, Вы писали:

K>Ну, если ты не можешь найти предыдущее сообщение в этой теме, то я понимаю почему эта тема в принципе появилась


Ты не написал "предыдущее" до этого.
Ответ простой — во первых, пример даже и близко не похож по распространенности. Во вторых, пофиксить эту проблему практически невозможно, а запретить ref types для ConcurrentDictionary — элементарно.

K>Мог бы возглавить стадо и показать как писать правильно, но почему-то не хочешь.


Мне дел и так хватает по горло.
Ад пуст, все бесы здесь.
Re[19]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 07:08
Оценка: +1 :)
Здравствуйте, Codealot, Вы писали:

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


S>> Опять же все lock-free основаны на Interlocked, Volatile а он и есть Memory Barrier Lock-free структуры данных. Основы: откуда пошли быть барьеры памяти


C>А предыдущий оратор писал, что нельзя на это полагаться. Так какие претензии ко мне тогда?

Нельзя полагаться на что? За доступ к свойствам объекта из многопотока отвечаешь ты
и солнце б утром не вставало, когда бы не было меня
Re[11]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 07:12
Оценка: +2
Здравствуйте, Codealot, Вы писали:

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


S>>Да, нужно.


C>Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.

Такого не может быть ибо за ссылку и объект отвечает GC. Есть WeakReference но сам то WeakReference существует.
и солнце б утром не вставало, когда бы не было меня
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 23.02.22 01:57
Оценка: -2
Здравствуйте, Sinclair, Вы писали:

S>там написана чушь.

S>присваивание ссылки куда бы то ни было выполняется после того, как отработает конструктор.

Для человека который никогда не слышал про memory read/write reordering у тебя слишком много понтов
Ад пуст, все бесы здесь.
Re[3]: ConcurrentDictionary vs reference type
От: Ночной Смотрящий Россия  
Дата: 23.02.22 18:39
Оценка: +2
Здравствуйте, Codealot, Вы писали:

C>Не люблю людей, которые ничего не знают но очень веское мнение имеют.


Себя тоже не любишь? А то прям точное описание твоего поведения здесь.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[17]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.02.22 03:44
Оценка: +2
Здравствуйте, Codealot, Вы писали:


C>Любой, в котором ты создаешь объект в одном треде, читаешь в другом и не делаешь никаких добавочных действий, чтобы избавиться от реордеринга.

Конкретный пример будет?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 24.02.22 22:38
Оценка: :))
Здравствуйте, xpalex, Вы писали:

X>

for static variables (other than those assigned RVAs within a PE file, see
X>Partition II) and objects...


X>Надо прокачивать скил чтения.


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

X>Не смог понять что там написано? Если целевая платформа не умеет в синхронизацию (в том числе памяти через memory fences), то кидать исключение.


В отличие от тебя — смог. Там написано, что рантайм должен сделать либо то, либо это. Больше ничего там по этому поводу не написано.

Так что покажи, где написано именно то что ты утверждаешь, а не то, что при очень большом желании и старании можно натянуть на глобус
Ад пуст, все бесы здесь.
Re[32]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 27.02.22 17:02
Оценка: :))
Здравствуйте, Serginio1, Вы писали:

S> Ну ты хоть пример то приведи! А то все болтовня!


Я не виноват, что до тебя не доходит.
Вот например почитай https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

S> угу ссылка=new Конструктор() откуда она видна из другого процесса, а вот куда в итоге это ссылка записывается там и нужна синхронизация


Откуда передается ссылка — тоже. Нет никакого "после создания", Нео.

S>Это по сути то и есть ConcurrentDictionary. Но никаких проблем с интернированными строками я не слышал!


Там совершенно определенно делается лок при каждом вызове String.Intern, о чем и написано в твоей же ссылке. Ты читать вообще умеешь?
Ад пуст, все бесы здесь.
Re[19]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 13:10
Оценка: +2
Здравствуйте, ·, Вы писали:

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


S>>Разумеется, CIL генератор не обязан это делать, если с этим справляется CLI или железо этого не требует. Эта спека все-таки про модель памяти, а не про CIL генератор.

·>Оригинальный вопрос был именно про генератор c#. Причём тут CLI и обнуление памяти — неясно. Зачем привели эту цитату — неясно.
Оригинальный вопрос был про то, нужен ли бубен при работе с ConcurrentDictionary. Генератор там ну никак не упоминался, если, конечно он не назван бубном. Но вряд ли.

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

·>Ок. Хорошо, но уже лучше. А вот такой пример тоже работает так же? Если он работает как-то иначе, плиз ссылку в стандарт.

·>
·>class Example {
·>  int value;
  
·>  public Example() {
·>    this.value = 42;
·>  }
·>}
·>...
·>observer.sendToAnotherThread(new Example());// вот тут другой тред точно увидит 42 или может быть и 0??
·>

я это вижу таким образом, что либо conforming implementation of the CLI, либо CIL generators должны нам обеспечить 42.

S>>Но sendToAnotherThread не имеет магического способа заставить другой поток работать с этими данными по ссылке. Или этой магии придется обращаться уже к неким функциям управления потоками, а уж они обеспечат видимость измененного в конструкторе состояния другим потокам. В любом случае this придется опубликовать.

·>_заставить_ одно дело. Другое дело, что ссылка может быть передана и без _заставить_ (например через запись-чтение non-volatile переменной), и другой тред неожиданно увидит 0. А _заставление_ произойдёт немного позже явным барьером, например. Иными словами, барьер обязует лишь то, что изменение будет увиденно, но никто не гарантирует, что если барьера (ещё) нет, то и никто не увидит.
Если конструктор отработал, то ссылаемся на реализацию CLI или генераторов. Если ссылка протекла в другой тред до завершения конструктора — то как минимум, на этот текст мы опираться не можем. Но в оригинале же речь о том, можно ли без бубна класть объект в ConcurrentDictionary и потом доставать его оттуда, не боясь хтонических ужасов. В итоге, если мы в словарь положим каким-то чудом протекшую ссылку на объект, чей конструктор еще не выполнен, то надеяться остается лишь на реализацию словаря. Однако, хтонический ужас уже в том, что мы в словарь пытаемся положить ссылку на объект, чей конструктор еще не выполнился. А значит, никто не отвечает не то, что бы за барьер памяти, а тупо состояние объекта может быть незаполнено еще тем потоком, который выполняет конструктор. Т.е. достать из словаря мы имеем право тоже объект, чей конструктор еще не выполнился.

Так причем тут словарь вообще и документация к нему? Даже если мы руками выполним барьер памяти, это не приведет к принудительному завершению конструктора в другом треде и заполнению полей объекта.
Re[27]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 20:26
Оценка: +1 -1
Здравствуйте, ·, Вы писали:

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


S>>·>Перечитай что я написал. Я ничего не говорил о том, что в ConcurrentDictionary есть проблема. Я говорил, что проблема может быть в коде, который использует ConcurrentDictionary, но эта проблема не видна, т.к. замаскирована барьерами в ConcurrentDictionary. Т.е. присваивание в конструкторе будет работать правильно только с конкретной имплементацией платформы.

·>Я написал "конкретную", а ты читаешь "корректную". Перечитай, что я пишу. А теперь ещё раз внимательно, прежде чем в очередной раз ответить невпопад.
Прости, виноват, невнимателен. Конкретная имплементация... Хорошо, пусть будет конкретная. Но когда будет еще одна конкретная имплементация, то целью второй имплементации будет повторение поведения первой конкретной, там где это возможно. А где невозможно — вот уж там надо будет писать на каждом углу и заборе, что что-то работает не так, как у конкретной, которая уже есть и к которой все привыкли за почти 20 лет (CLR 2.0 и последующие).

S>>Ну так в рамках CLR 1.0 ему придется отказаться от ConcurrentDictionary вобоще. Не заведется, т.к. там нужен 2.0 минимум, хотя бы потому, что генерик. И с любой other implementation or CLR следовало бы уточнить для начала это, а не называть всех кретинами.

·>Стиль его общения, конечно, не очень, но это не отменяет проблему.
А есть ли проблема? У нас есть конкретная платформа, в рамках которой все работает (пока не приведен код, демонстрирующий обратное). И нет другой конкретной платформы, где бы это не работало.

S>>и много ли потеряли?

·>Это другой вопрос. Я не знаю. Думаю, от аппаратной платформы зависит.
наверняка. Но не думаю, что это проблема. Т.е. проблема, конечно. Но ее решение может быть в чем? Вводить инструкции для разного железа и заставлять их учить всех, кто пишет на дотнете, но даже у кого нет и не будет такого железа, которое потребует знания специфики?

S>>Не ничего не обеспечивает, а не назван конкретный механизм, обеспечивающий это.

·>Я вообще не вижу, что там названы вообще хоть какие-то конкретные механизмы, этого в стандарте и не должно быть. Главное то, что в стандарте не написано, что этот механизм должен существовать.
Он упомянут. И названы два возможных места, где он может быть реализован. Возможно, есть еще места, но в голову не приходит, где это могло бы еще быть реализовано так, что бы мог работать общий код на IL и выше.
Я понимаю, тебя смущает что не написано, что 42 обязано быть. Меня тоже, но это уже как бы ложится на плечи тех ребят, которые будут делать другую конкретную имплементацию. Это, и наверное еще много чего другого, о чем тут не говорили. Но и не проси примеры. Я просто предполагаю.

S>>Точно не уровень языка. CLI или CIL генератор.

·>Может я точно не знаю, но как я понимаю, CIL generator это такая штука, которая из c# генерит IL-инструкции. И то какие инструкции будут сгенерированы (в данном случае нас интересуют барьеры), определяются исходным кодом и семантикой c#.
Вообще-то я был уверен что CIL генератор это что-то вроде NGen-а, но NGen — специфичен для конкретной платформы и его в стандарте постеснялись указать явно. Вот уж точно стандарт CLI не должен ссылаться на конкретные языки выше IL уровнем и их отображение в CIL. Кстати, этим занимаются просто компиляторы. Компилятор C#, F#, VB и т.п. И так уже много лет, все к этому привыкли. Может быть с рослином что-то поменялось, но текст, который мы пытаемся осилить — старше рослина. Найти, что конкретно подразумевается под CIL generator в том тексте — не осилил. Сложный день был.
Re[24]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 16:29
Оценка: 4 (1)
Здравствуйте, samius, Вы писали:

S>>>не поспоришь

S>·>Плюс идея в том, что наличие барьеров в текущей имплементации ConcurrentDictionary может маскировать проблему. Что-нибудь может измениться внезапно (оставаясь в рамках документированного контракта ConcurrentDictionary), и полезут интересные баги.
S>И это не проблема ConcurrentDictionary и его контракта, т.к. у нас есть несколько Concurrent* классов, да и не только они.
Перечитай что я написал. Я ничего не говорил о том, что в ConcurrentDictionary есть проблема. Я говорил, что проблема может быть в коде, который использует ConcurrentDictionary, но эта проблема не видна, т.к. замаскирована барьерами в ConcurrentDictionary. Т.е. присваивание в конструкторе будет работать правильно только с конкретной имплементацией платформы. Собственно, so тоже согласен, что это implementation defined:

It is not specified in ECMA standard, it is just the microsoft implementation of the CLR gives this guarantee. If you run this code in either CLR 1.0 or any other implementation of CLR, your code is likely to break.

Собственно, как я понял, Codealot это и имел в виду.

И сделано это ценой потери перформанса:

unfortunate that synchronization cost caused by this CLR2.0 guarantee is payed by all code, yet typically only <1% of code deals with threading at all.


S>·>Да ё-моё, ты всё смешал. Про CIL написано о конструкторах, а про CLI написано о zeroeing. Т.е. из цитаты следует, что 0 будет точно (т.е. не будет случайных значений из неинициалированного куска памяти как бывает в Плюсах, например), но гарантии что будет 42 я в упор не вижу.

S>Так в описании CLI тебе эту гарантию и не дают, но указывают, что этим могут заниматься сущности за пределом ответственности CLI. При необходимости, разумеется.
Твоё заявление: "CIL generators должны нам обеспечить 42", неверно, это ошибка. Ничего в стандарте 42 не обеспечивает, только текущие детали имплементации платформы от microsoft это обещают.

S>>>C#, на сколько я помню, тут вообще не при чем. И если даже мы найдем в спеке языка что-то, то вопрос о том, будет ли это исполняться для VB.NET или F#, или еще какого-то дотнет языка — останется открыт.

S>·>Не смешивай consructors и zeroeing. C# тут притом что он обеспечивает конструкторы. А CIL обеспечивает аллокацию памяти и инициализацию её нулями.
S>C# тут лишний, т.к. конструкторы бывают и без C#. В C++/CLI или даже просто в IL тоже есть конструкторы.
Ну и? Кто из них должен обеспечить 42 по-твоему?!
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 28.02.2022 16:34 · . Предыдущая версия .
Re[3]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 23:27
Оценка: +1
Здравствуйте, Codealot, Вы писали:

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


S>>Все, что гарантирует ConcurrentDictionary, написано в его документации и его гарантии никак не распространяются на все остальное.


C>PS если объект нельзя вообще использовать даже если ты просто его поместил и потом извлек, то что толку от такой коллекции? Наверно, какие-то гарантии всё же есть. Но документация про это умалчивает.


Я не понимаю, почему нельзя использовать объект от того, что я его куда-то поместил и потом извлек? С объектом ничего не происходит в момент добавления. В чем проблема его использовать?
Re[8]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 23:58
Оценка: :)
Здравствуйте, samius, Вы писали:

S>Я пишу код, исходя из того, что словарь работает, пока никто не доказал обратное.


Ты не знаешь, кто конкретно там работает и какие гарантии даются.

S>Какое отношение имеет реордеринг в реализации словаря к объектам, которые я в него добавляю по ссылке?


Я говорю о реордеринге памяти. Ты вообще понял, о чем идет речь?
Ад пуст, все бесы здесь.
Re[9]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 15:58
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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

C>Если он таких гарантий не дает, то получив ссылку на объект в другом треде, использовать эту ссылку ты не можешь. А это значит, что сохранять ссылки в словарь попросту нельзя. А это значит, что ссылки на объекты надо запретить в generic constraints класса. Доходит?

Понятно, о чем ты. Не понятно, причем тут ConcurrentDictionary и его документация, если ты опасаешься передать ссылку на объект в другой тред.

https://github.com/stakx/ecma-335/blob/master/docs/i.12.6.8-other-memory-model-issues.md
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 17:22
Оценка: :)
Здравствуйте, karbofos42, Вы писали:

K>Я там дополнил своё сообщение примером с "обычным" словарём.


Я должен искать, где и что ты там дополнил?

K>Ничем нельзя пользоваться выходит, нужно всё своё писать правильное.


Я таки единственный солдат во всей израильской армии?
Ад пуст, все бесы здесь.
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 17:22
Оценка: -1
Здравствуйте, samius, Вы писали:

S>Понятно, это на уровне конкретного CPU, и в модели памяти дотнета (и джавы тоже) такого нет.


И в ней тоже есть. Просто ты не знаешь базовые концепции многопоточности, вот и всё.
Ад пуст, все бесы здесь.
Re[15]: ConcurrentDictionary vs reference type
От: karbofos42 Россия  
Дата: 19.02.22 18:59
Оценка: +1
Здравствуйте, Codealot, Вы писали:

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


K>>Я там дополнил своё сообщение примером с "обычным" словарём.


C>Я должен искать, где и что ты там дополнил?


Ну, если ты не можешь найти предыдущее сообщение в этой теме, то я понимаю почему эта тема в принципе появилась

K>>Ничем нельзя пользоваться выходит, нужно всё своё писать правильное.


C>Я таки единственный солдат во всей израильской армии?


Да не. Просто все дураки, а ты один Д'Артаньян, просёк фишку.
Мог бы возглавить стадо и показать как писать правильно, но почему-то не хочешь.
Re[15]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.02.22 19:54
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>> Его задача положить достать. А вот за Thread.MemoryBarrier отвечаешь ты.


C>Звать Thread.MemoryBarrier еще раз, когда он уже вызывается при добавлении — поставить крест на производительности. Так что либо гарантируется, что класс всегда его вызывает, либо это всё не имеет никакого смысла.

Ты должен сам защитить переменные класса.

Thread.MemoryBarrier
Синхронизирует доступ к памяти следующим образом: процессор, выполняющий текущий поток, не способен упорядочить инструкции так, чтобы обращения к памяти до вызова метода MemoryBarrier() выполнялись после обращений к памяти, следующих за вызовом метода MemoryBarrier().
public static void MemoryBarrier ();
Комментарии
для большинства целей lock оператор C#, SyncLock оператор Visual Basic или Monitor класс предоставляют более простые способы синхронизации данных.


Еще раз почитай
volatile
Ну и стоит почитать Доступ к локальной переменной из разных потоков
Автор: Aquilaware
Дата: 18.11.20


Там есть и Volatile.Write,Volatile.Read Thread.MemoryBarrier
Ну и Lock то никто не отменял!
Ну и внутри используется Volatile.Read

public bool TryGetValue(TKey key, out TValue value)
    {
      if ((object) key == null)
        throw new ArgumentNullException(nameof (key));
      ConcurrentDictionary<TKey, TValue>.Tables tables = this.m_tables;
      IEqualityComparer<TKey> comparer = tables.m_comparer;
      int bucketNo;
      this.GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out int _, tables.m_buckets.Length, tables.m_locks.Length);

.....
      for (ConcurrentDictionary<TKey, TValue>.Node node = Volatile.Read<ConcurrentDictionary<TKey, TValue>.Node>(ref tables.m_buckets[bucketNo]); node != null; node = node.m_next)
      {
        if (comparer.Equals(node.m_key, key))
        {
          value = node.m_value;
          return true;
        }
      }
      value = default (TValue);
      return false;
    }



Внутри таблицы есть массив объектов для блокировки internal readonly object[] m_locks;
И блокируется только цепочка с одинаковым lockNo
bucketNo = (hashcode & int.MaxValue) % bucketCount;
      lockNo = bucketNo % lockCount;


 private bool TryAddInternal(
      TKey key,
      TValue value,
      bool updateIfExists,
      bool acquireLock,
      out TValue resultingValue)
    {
label_0:

      this.GetBucketAndLockNo(hashCode, out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
 
      try
      {
        if (acquireLock)
          Monitor.Enter(tables.m_locks[lockNo], ref lockTaken);

        if (tables == this.m_tables)
        {
          int num = 0;
          ConcurrentDictionary<TKey, TValue>.Node node1 = (ConcurrentDictionary<TKey, TValue>.Node) null;
          for (ConcurrentDictionary<TKey, TValue>.Node node2 = tables.m_buckets[bucketNo]; node2 != null; node2 = node2.m_next)


Вот, что подобное ты и должен сделать со своим классом, что бы уменьшить конкуренцию использовать примитивных типов
Там можно использовать и Interlocked https://docs.microsoft.com/ru-ru/dotnet/api/system.threading.interlocked?view=net-5.0
и солнце б утром не вставало, когда бы не было меня
Отредактировано 27.02.2022 18:11 Serginio1 . Предыдущая версия . Еще …
Отредактировано 27.02.2022 18:09 Serginio1 . Предыдущая версия .
Re[16]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 20:11
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S>Еще раз почитай

S>volatile

Ты когда-нибудь интересовался, сколько он стоит?

S>Ну и Lock то никто не отменял!


Отож. Делаем лок при любых операциях с объектом, а ConcurrentDictionary попросту списываем в утиль, потому что он в этом сценарии абсолютно бесполезен.

А теперь вопрос на миллион: почему ConcurrentDictionary позволяет работать с объектами, если на самом деле этот юз кейс некорректен?
Ад пуст, все бесы здесь.
Re[12]: ConcurrentDictionary vs reference type
От: StatujaLeha на правах ИМХО
Дата: 20.02.22 19:42
Оценка: +1
Здравствуйте, karbofos42, Вы писали:

K>И ключ словаря менять разрешают и два элемента словаря под одним ключом записывать... О чём они там вообще думают?


https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2?view=net-5.0

As long as an object is used as a key in the Dictionary<TKey,TValue>, it must not change in any way that affects its hash value. Every key in a Dictionary<TKey,TValue> must be unique according to the dictionary's equality comparer. A key cannot be null, but a value can be, if its type TValue is a reference type.

Re[9]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 22.02.22 01:34
Оценка: +1
Здравствуйте, Codealot, Вы писали:

C>Если допустить что это действительно так, то класть объекты в CD нельзя. Нужно объяснять, почему?

Да, нужно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[21]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 07:06
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>> Ну а как ты хочешь работать в многопотоке?

S>>Твои решения

C>Барьер, в данном случае. Но если он уже вызывается в коллекции, то второй раз не нужно. Но полагаться на то что он там вызывается вроде как нельзя

Нужно. Это касается только коллекции, но не объектов.
За доступ к свойствам объектов отвечаешь ты
Барьеры памяти и неблокирующая синхронизация в .NET
и солнце б утром не вставало, когда бы не было меня
Отредактировано 22.02.2022 7:59 Serginio1 . Предыдущая версия .
Re[19]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 07:10
Оценка: +1
Здравствуйте, Codealot, Вы писали:

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


S>>Он не меняется, а создается 1 раз в конструкторе!


C>Это не отменяет необходимости изменить память, где он будет лежать.

Нет не надо. Ибо свойства то не меняются и по барабану где лежит ссылка и объектные свойства в регистре, кэше или памяти!
и солнце б утром не вставало, когда бы не было меня
Re[13]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 17:07
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>> Такого не может быть ибо за ссылку и объект отвечает GC. Есть WeakReference но сам то WeakReference существует.


C>Ты явно не понимаешь, как это всё работает. В том треде, который создает объект — да, там всё есть. В других тредах может быть видно практически любое случайное частичное подмножество из этих данных. Абсолютно никаких гарантий по этому вопросу .NET не дает.

Ну я же тупой. Разъсни как это string не виден в других тредах?
GC при сборке мусора останавливает потоки.


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

и солнце б утром не вставало, когда бы не было меня
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 17:11
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S>GC при сборке мусора останавливает потоки.


Да. Останаваливает и синхронизирует всё что можно, прежде чем собирать мусор. Я надеюсь ты не думаешь, что при создании каждого объекта тоже синхронизируется кэш всех процессоров, к примеру?
Ад пуст, все бесы здесь.
Re[21]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 17:32
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>> Нельзя полагаться на что? За доступ к свойствам объекта из многопотока отвечаешь ты


C>На то, что там барьер памяти. Так можно полагаться на то что он там есть или нет?

Как я тебе привел ниже есть несколько типов барьеров памяти. Основной это компилятор. Что бы не засунули переменную в регистр.
Как уже тут отмечали когерентность кэшей процессора достаточно и отсутствия оптимизаций кода компилятором.
и солнце б утром не вставало, когда бы не было меня
Re[22]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 17:42
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S> Как я тебе привел ниже есть несколько типов барьеров памяти. Основной это компилятор. Что бы не засунули переменную в регистр.


Компилятор дает какие-либо гарантии только для текущего треда.

S>Как уже тут отмечали когерентность кэшей процессора достаточно и отсутствия оптимизаций кода компилятором.


Нет, недостаточно. К примеру, без добавочных телодвижений у тебя нет никаких гарантий, что тело объекта было записано в память до ссылки на него, а не после.
Ад пуст, все бесы здесь.
Re[22]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 17:43
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S> И что меняется для иммутабельного объекта?


Я с тебя просто офигеваю. Ты вообще в курсе, что объект и ссылку(и) на него надо записать в память, когда он создается?
Ад пуст, все бесы здесь.
Re[13]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 23.02.22 01:42
Оценка: +1
Здравствуйте, Codealot, Вы писали:

C>Re[12]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 22.02.22

там написана чушь.
присваивание ссылки куда бы то ни было выполняется после того, как отработает конструктор.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[27]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 23.02.22 11:30
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


C>И охота тебе ветки плодить. Соберу все сюда.


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

S>>Как Interlocked.MemoryBarrier в другом методе может на это повлиять?

C>Хз, как оно работает с регистрами я не копал. Но Interlocked.MemoryBarrier делает синхронизацию памяти только для процессора текущего треда. Если нужно сделать это для всех процессоров, то для этого есть MemoryBarrierProcessWide, который работает намного медленнее. Для регистров там тоже что-то должно быть, иначе никакой многотредный код вообще бы не работал.


Там не синхронизация а упорядочивние инструкций
Барьер памяти

Барьер памяти (англ. memory barrier, membar, memory fence, fence instruction) — вид барьерной инструкции, которая приказывает компилятору (при генерации инструкций) и центральному процессору (при исполнении инструкций) устанавливать строгую последовательность между обращениями к памяти до и после барьера. Это означает, что все обращения к памяти перед барьером будут гарантированно выполнены до первого обращения к памяти после барьера.



S>>Это как? Конструктор это как раз гарантирует.


C>Приведи цитату, где написано что что-то в этом духе гарантируется для всех тредов, а не только для текущего.

А он создается только в одном потоке! Ты не можешь применить async к конструктору
S>> Это я с тебя охреневаю. Конструктр отдает ссылку после того как создан и заполен (инициализирован)объект

C>Сначала выделяется память и сохраняется ссылка на нее, потом заполняются поля объекта.

Угу ты хоть ссылки приводи на свои мысли!
А тебе уже приводили ссылку http://rsdn.org/forum/dotnet/8203957.1
Автор: xpalex
Дата: 23.02.22

Конструктор то как раз и создан для инициализации объекто до получения ссылки!
S>>Ссылка получается после выделения и заполнения памяти, но никак по другому. Если же ты заполняешь после конструктора без синхронизации то это уже твои проблемы!

C>Тебе известны какие-то спеки, которые говорят о таких гарантиях для всех тредов?

C>Мне известны спеки, которые говорят о прямо противоположном.

C>https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/december/csharp-the-csharp-memory-model-in-theory-and-practice

C>

C>One source of complexity in multithreaded programming is that the compiler and the hardware can subtly transform a program’s memory operations in ways that don’t affect the single-threaded behavior, but might affect the multithreaded behavior. Consider the following method:
C>XML

C>void Init() {
C> _data = 42;
C> _initialized = true;
C>}

C>If _data and _initialized are ordinary (that is, non-volatile) fields, the compiler and the processor are allowed to reorder the operations so that Init executes as if it were written like this:
C>XML

C>void Init() {
C> _initialized = true;
C> _data = 42;
C>}

C>The C# memory model permits reordering of memory operations in a method, as long as the behavior of single-threaded execution doesn’t change.


И где конструктор то? Ещер раз конструктор создается только в одном потоке и получает ссылку после инициализации конструктором.


S>>При создании объекта в конструкторе другие потоки его не читают ибо ссылки то нет. А вот если после создания какое то поле поместили в регистр, то вот здесь и надо предусмотреть эту ситуацию.


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


А те бе не похрен в каком порядке инициализировна память? Она уже законнчена. Проблема с переупорядочиванием только когда одновременно пишем в одном потоке и читаем в друго.
При создании в конструкторе никакого чтения из другого потока не происходит! Если конечно не передать this в другой поток до полной инициализации. Но тут уж ты CCЗС
и солнце б утром не вставало, когда бы не было меня
Отредактировано 23.02.2022 11:43 Serginio1 . Предыдущая версия .
Re[28]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 23.02.22 17:19
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S> Там не синхронизация а упорядочивние инструкций


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

S> А он создается только в одном потоке! Ты не можешь применить async к конструктору


Создается в одном, читается в другом. Кроме того, даже в пределах одного треда рантайм может делать реордеринг — если это не мешает ничему в этом треде.

S>Конструктор то как раз и создан для инициализации объекто до получения ссылки!


Нельзя инициализировать объект, не имея ссылку на память где он расположен. Даже это тебе непонятно?

S> конструктор создается только в одном потоке и получает ссылку после инициализации конструктором.




S>А те бе не похрен в каком порядке инициализировна память? Она уже законнчена. Проблема с переупорядочиванием только когда одновременно пишем в одном потоке и читаем в друго.

S>При создании в конструкторе никакого чтения из другого потока не происходит! Если конечно не передать this в другой поток до полной инициализации. Но тут уж ты CCЗС

Пц. А до тебя не доходит, что когда ты передаешь this в другой поток после завершения инициализации — у тебя нет никаких гарантий, что другой тред увидит все обновления данных в памяти?
Ад пуст, все бесы здесь.
Re[29]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 23.02.22 17:52
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>> Там не синхронизация а упорядочивние инструкций


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

Барьер ты как раз и вызываешь, что бы компилятор не оптимизировал. Но volatile этим и занимется без полного барьера
S>> А он создается только в одном потоке! Ты не можешь применить async к конструктору

C>Создается в одном, читается в другом. Кроме того, даже в пределах одного треда рантайм может делать реордеринг — если это не мешает ничему в этом треде.

Иии. Это чему мешает например для имутабельных классов?
S>>Конструктор то как раз и создан для инициализации объекто до получения ссылки!

C>Нельзя инициализировать объект, не имея ссылку на память где он расположен. Даже это тебе непонятно?

Не непонятно. ССылка когда становится доступной? Только после создания и инициализации в конструкторе. Если ты конечно из контруктора её не передаешт. Но это твои проблемы
S>> конструктор создается только в одном потоке и получает ссылку после инициализации конструктором.

C>


S>>А те бе не похрен в каком порядке инициализировна память? Она уже законнчена. Проблема с переупорядочиванием только когда одновременно пишем в одном потоке и читаем в друго.

S>>При создании в конструкторе никакого чтения из другого потока не происходит! Если конечно не передать this в другой поток до полной инициализации. Но тут уж ты CCЗС

C>Пц. А до тебя не доходит, что когда ты передаешь this в другой поток после завершения инициализации — у тебя нет никаких гарантий, что другой тред увидит все обновления данных в памяти?


Ну вот строки прекрасно работают и нет никаких проблем с потоками. Ты хоть примеры приводи. А то болтать не мешки ворочать. Что то сам себе придумал без ссылок.
и солнце б утром не вставало, когда бы не было меня
Re[30]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 24.02.22 22:38
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S>Барьер ты как раз и вызываешь, что бы компилятор не оптимизировал. Но volatile этим и занимется без полного барьера


А также для того, чтобы синхронизировать память. Если они вызываются.

S> Иии. Это чему мешает например для имутабельных классов?


Всему.

S> Не непонятно. ССылка когда становится доступной? Только после создания и инициализации в конструкторе. Если ты конечно из контруктора её не передаешт. Но это твои проблемы


Нет никакого "после создания", Нео.

S> Ну вот строки прекрасно работают и нет никаких проблем с потоками.


Где конкретно, в каком сценарии и что это доказывает?
Ад пуст, все бесы здесь.
Re[25]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 16:44
Оценка: -1
Здравствуйте, ·, Вы писали:

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


S>>И это не проблема ConcurrentDictionary и его контракта, т.к. у нас есть несколько Concurrent* классов, да и не только они.

·>Перечитай что я написал. Я ничего не говорил о том, что в ConcurrentDictionary есть проблема. Я говорил, что проблема может быть в коде, который использует ConcurrentDictionary, но эта проблема не видна, т.к. замаскирована барьерами в ConcurrentDictionary. Т.е. присваивание в конструкторе будет работать правильно только с конкретной имплементацией платформы.
Извини, но мы тут про корректную имплементацию платформы и говорим. Если тебе нужна некорректная имплементация, то ты ошибся форумом, кмк. Не хотел обидеть, если что.

.>Собственно, so тоже согласен, что это implementation defined:

·>

It is not specified in ECMA standard, it is just the microsoft implementation of the CLR gives this guarantee. If you run this code in either CLR 1.0 or any other implementation of CLR, your code is likely to break.

·>Собственно, как я понял, Codealot это и имел в виду.
Ну так в рамках CLR 1.0 ему придется отказаться от ConcurrentDictionary вобоще. Не заведется, т.к. там нужен 2.0 минимум, хотя бы потому, что генерик. И с любой other implementation or CLR следовало бы уточнить для начала это, а не называть всех кретинами.

·>И сделано это ценой потери перформанса:

·>

unfortunate that synchronization cost caused by this CLR2.0 guarantee is payed by all code, yet typically only <1% of code deals with threading at all.

и много ли потеряли?

S>>Так в описании CLI тебе эту гарантию и не дают, но указывают, что этим могут заниматься сущности за пределом ответственности CLI. При необходимости, разумеется.

·>Твоё заявление: "CIL generators должны нам обеспечить 42", неверно, это ошибка. Ничего в стандарте 42 не обеспечивает, только текущие детали имплементации платформы от microsoft это обещают.
Не ничего не обеспечивает, а не назван конкретный механизм, обеспечивающий это.

S>>·>Не смешивай consructors и zeroeing. C# тут притом что он обеспечивает конструкторы. А CIL обеспечивает аллокацию памяти и инициализацию её нулями.

S>>C# тут лишний, т.к. конструкторы бывают и без C#. В C++/CLI или даже просто в IL тоже есть конструкторы.
·>Ну и? Кто из них должен обеспечить 42 по-твоему?!
Точно не уровень языка. CLI или CIL генератор.
Re[43]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.03.22 16:50
Оценка: :)
Здравствуйте, Codealot, Вы писали:

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


S>>readonly важно тем, что инициализируешь только в конструкторе. И они потокобезопасны! Их нельзя менять!

S>>А в однопоточном конструкторе глубоко наплевать на Memory reordering!

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

Ну и меняют они в конструкторе. Объясни в чем проблема?
S>> Приведи пример. Тебя вообще мало кто понимает.

C>Топик такой, что его вообще крайне мало кто понимает. Хотя тех, кто думает что понимает — очень много.

Угу тебе уже кучу доводов привели. И про voliatil написали. Но у тебя какие то тараканы в голове и мешанина.
Ты так и не привел примера

ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only), или нужны какие-то дополнительные движения с бубном?
Документация чего-то не рассматривает этот вопрос


ConcurrentDictionary гарантирует потокобезопасное запись и чтение из словаря. Потокобезопасность объекта ты должен обеспечить сам!
и солнце б утром не вставало, когда бы не было меня
Re[46]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 17:15
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S> Оно не readonly! int GetInt() не конструктор!


Проблема — в new BoxedInt(42) и ссылке на объект. Напряги мозги.

S>GetInt объекта может вызываться из разных потоков!


Не может быть!
Ад пуст, все бесы здесь.
Re[48]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 17:51
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S> Нет проблема в

S>
S>_boxedInt = b;
S>


Докажи.

S>На большинстве платформ (точнее говоря, на всех платформах, поддерживаемых Windows, кроме умирающей IA64) все записи и чтения являются volatile write и volatile read соответственно.


Какой забористый бред.
Ад пуст, все бесы здесь.
Re[49]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 10.03.22 01:55
Оценка: +1
Здравствуйте, Codealot, Вы писали:

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


X>>Т.е ты приводишь какой-то код и просишь найти где в нем что-то может пойти не так с точки зрения твоего незнания memory model CLR 2.0


C>Это из той же ссылки, которую я давал тебе лично и которую ты очевидно не читал. https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2


Ты ниже кода, котрый ты скопипастил из статьи не прочитал что-ли?

A>However, this code will behave correctly (that is, always return 42) on all architectures in the .NET Framework versions 4 and 4.5:

A>
A>x86-x64:
A> Writes and reads will not be reordered. There is no store-load pattern in the code, and also no reason the compiler would cache values in registers.
A>Itanium:
A> Writes will not be reordered because they are ST.REL.
A> Reads will not be reordered due to data dependency.
A>ARM:
A> Writes will not be reordered because DMB is emitted before “_boxedInt = b.”
A> Reads will not be reordered due to data dependency.

Вообще пример про разъяснение реордеринга плохой — там в каждой строчке использование переменной b. А реордеринг разрешен только для инструкций с независимыми переменными.

C>Кстати, с чего ты решил что свет сошелся клином именно на версии 2.0? Это единственная, которую ты знаешь?

А в CLR 4 (2010 год) в отношении гарантий по памяти ничего не поменялось. CLR 3 не было, CLR 5+ еще не было. Или ты не знал?

C>Один тред создает объект и передает ссылку на него в другой тред. Другой тред, который читает новый объект, прочитал ссылку раньше чем обновленные конструктором поля объекта. Не доходит?

Для с++,c да и любого языка, который наиболее близко компилируется в нейтив код, такая ситуация верна.
Для .net, который компилируется в CIL и только потом CLR-ом в нейтив — нет. Т.к. CLR добавляет барьеры для платформ там где требуется.

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

Тогда уточни вопрос из первого сообщения темы: что за опасность подстерегает объекты, когда они хранятся в ConcurrentDictionary?
А прям идеально было бы кусок кода, который ведет себя некорректно при использовании ConcurrentDictionary.

Или
C>ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only), или нужны какие-то дополнительные движения с бубном?
это просто про потрепаться?
ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 22:12
Оценка:
ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only), или нужны какие-то дополнительные движения с бубном?
Документация чего-то не рассматривает этот вопрос
Ад пуст, все бесы здесь.
Отредактировано 18.02.2022 22:20 Codealot . Предыдущая версия .
Re[2]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 22:28
Оценка:
Здравствуйте, samius, Вы писали:

S>Все, что гарантирует ConcurrentDictionary, написано в его документации и его гарантии никак не распространяются на все остальное. Если в его значениях ссылки, то словарь гарантирует что ссылка будет приписана к значению. А каким образом там пользователь словаря работает с тем, на что ссылка ссылается — не словаря дело.


Да ничего там толком не написано. Например, есть ли там какой-то барьер памяти при добавлении?
Ад пуст, все бесы здесь.
Re[3]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 18.02.22 22:40
Оценка:
Здравствуйте, Codealot, Вы писали:

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


C>Да ничего там толком не написано. Например, есть ли там какой-то барьер памяти при добавлении?

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

Но да, использует, если поглядеть в код.
Re[2]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 23:22
Оценка:
Здравствуйте, samius, Вы писали:

S>Все, что гарантирует ConcurrentDictionary, написано в его документации и его гарантии никак не распространяются на все остальное.


PS если объект нельзя вообще использовать даже если ты просто его поместил и потом извлек, то что толку от такой коллекции? Наверно, какие-то гарантии всё же есть. Но документация про это умалчивает.
Ад пуст, все бесы здесь.
Re[4]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 18.02.22 23:28
Оценка:
Здравствуйте, samius, Вы писали:

S>Я не понимаю, почему нельзя использовать объект от того, что я его куда-то поместил и потом извлек? С объектом ничего не происходит в момент добавления. В чем проблема его использовать?


Поместил на одном ядре, извлек на другом. Что там и в каком порядке и когда пишется/читается — это такая загадка, от которой свихнулся бы даже сам Хокинг.
Ад пуст, все бесы здесь.
Re[10]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 15:15
Оценка:
Здравствуйте, samius, Вы писали:

S>Про реордеринг инструкций — слышал. Про барьеры памяти — тоже. Реордерниг памяти — о чем ты?


Есть такие штуки — кэш-линии. Хотя бы про них ты слышал?
Ад пуст, все бесы здесь.
Re[8]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 15:15
Оценка:
Здравствуйте, karbofos42, Вы писали:

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


Там не ответов на вопросы, которые я задал.
Ад пуст, все бесы здесь.
Re[8]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 15:15
Оценка:
Здравствуйте, samius, Вы писали:

S>Мыло-мочало, начинай сначала


Я не виноват, что ты не понимаешь о чем говоришь.
Ад пуст, все бесы здесь.
Re[10]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 16:05
Оценка:
Здравствуйте, samius, Вы писали:

S>Понятно, о чем ты. Не понятно, причем тут ConcurrentDictionary и его документация, если ты опасаешься передать ссылку на объект в другой тред.


Плохо, что непонятно.
Тебе не приходило в голову, что ConcurrentDictionary именно для того и существует, чтобы использовать его из разных тредов? И если он позволяет использовать объекты — то либо он дает какие-то гарантии по поводу их извлечения, либо его разработчики крупно накосячили?
Ад пуст, все бесы здесь.
Re[10]: ConcurrentDictionary vs reference type
От: vsb Казахстан  
Дата: 19.02.22 16:10
Оценка:
Здравствуйте, samius, Вы писали:

S>Реордерниг памяти — о чем ты?


Пусть изначально

global x := 0;
global y := 0;


В одном потоке выполняется

x := 1;
y := 2;



в другом потоке выполняется

local my_x := x;
local my_y := y;


Указанные инструкции выполняются на уровне машинного кода, т.е. компилятор ничего местами не поменял.

Реордеринг памяти это когда в my_x будет 0, а в my_y будет 2, хотя казалось бы логичны лишь варианты (0, 0) если до другого потока не дошли изменения; (1, 0) если дошло лишь первое изменение и (1, 2) если дошли оба изменения.

На x86 этой ситуации быть не может. На некоторых других архитектурах вроде ARM — может быть.
Re: ConcurrentDictionary vs reference type
От: vsb Казахстан  
Дата: 19.02.22 16:12
Оценка:
Здравствуйте, Codealot, Вы писали:

C>ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only), или нужны какие-то дополнительные движения с бубном?

C>Документация чего-то не рассматривает этот вопрос

Я думаю, у тебя горе от ума. Используй и не парься. Ну или почитай его исходники и убедись, что там всё хорошо.
Re[9]: ConcurrentDictionary vs reference type
От: karbofos42 Россия  
Дата: 19.02.22 16:13
Оценка:
Здравствуйте, Codealot, Вы писали:

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


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


C>Там не ответов на вопросы, которые я задал.


https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

All public and protected members of ConcurrentDictionary<TKey,TValue> are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentDictionary<TKey,TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.

Re[10]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 16:16
Оценка:
Здравствуйте, karbofos42, Вы писали:

K>https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

K>

K>All public and protected members of ConcurrentDictionary<TKey,TValue> are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentDictionary<TKey,TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.


Ты даже не понимаешь о чем идет речь, а туда же.
Имеется в виду потокобезопасность только самого объекта ConcurrentDictionary
Ад пуст, все бесы здесь.
Re[11]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 16:18
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>На x86 этой ситуации быть не может.


На это тоже не стоит слишком полагаться.
Ад пуст, все бесы здесь.
Re[11]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 16:20
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Понятно, о чем ты. Не понятно, причем тут ConcurrentDictionary и его документация, если ты опасаешься передать ссылку на объект в другой тред.


C>Плохо, что непонятно.

C>Тебе не приходило в голову, что ConcurrentDictionary именно для того и существует, чтобы использовать его из разных тредов? И если он позволяет использовать объекты — то либо он дает какие-то гарантии по поводу их извлечения, либо его разработчики крупно накосячили?

Давай уберем ConcurrentDictionary и подумаем, можно ли использовать обеъкты из одного потока в другом и почему об этом не пишут на каждому углу, либо разработчики круто накосячили один ты в белом....
Re[11]: ConcurrentDictionary vs reference type
От: karbofos42 Россия  
Дата: 19.02.22 16:32
Оценка:
Здравствуйте, Codealot, Вы писали:

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


K>>https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

K>>

K>>All public and protected members of ConcurrentDictionary<TKey,TValue> are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentDictionary<TKey,TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.


C>Ты даже не понимаешь о чем идет речь, а туда же.

C>Имеется в виду потокобезопасность только самого объекта ConcurrentDictionary

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

Я тебе наверно сейчас жизнь испорчу, но они в Dictionary и без потоков накосячили и этим классом тоже пользоваться нельзя.
    class MyKey
    {
        public int Value {get;set;}
        
        public MyKey(int value) { Value = value;}
        
        public override int GetHashCode() => Value;
        
        public override bool Equals(object other) => (other as MyKey)?.Value == Value;
    }
    
    public class Program
    {
        public static void Main(string[] args)
        {
            var dict = new Dictionary<MyKey, string>();
            var key = new MyKey(10);
            dict.Add(key, "string1");            
            key.Value = 20;
            dict.Add(key, "string2");
        }
    }

И ключ словаря менять разрешают и два элемента словаря под одним ключом записывать... О чём они там вообще думают?
Отредактировано 19.02.2022 16:43 karbofos42 . Предыдущая версия .
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 16:42
Оценка:
Здравствуйте, samius, Вы писали:

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


Нет, не давай уберем. Мы говорим о типе, созданном специально для многопоточности. И если простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. Будешь спорить?
Ад пуст, все бесы здесь.
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 16:42
Оценка:
Здравствуйте, karbofos42, Вы писали:

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

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

Я повторю еще раз, для самых быстрых.

Мы говорим о типе, созданном специально для многопоточности. И если в нем простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. А это значит, что generic constraint типа должен в явном виде их запретить. Не доходит?
Ад пуст, все бесы здесь.
Re[11]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.22 16:46
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Реордеринг памяти это когда в my_x будет 0, а в my_y будет 2, хотя казалось бы логичны лишь варианты (0, 0) если до другого потока не дошли изменения; (1, 0) если дошло лишь первое изменение и (1, 2) если дошли оба изменения.


Понятно, это на уровне конкретного CPU, и в модели памяти дотнета (и джавы тоже) такого нет.

vsb>На x86 этой ситуации быть не может. На некоторых других архитектурах вроде ARM — может быть.

Такое — верятно. Но речь ведь в данном треде немного о другом, почему можно взять из ConcurrentDictionary ссылку на объект и ей пользоваться... Но ответ как раз в модели памяти дотнета, а не в библиотечном классе ConcurrentDictionary или его документации.
Re[13]: ConcurrentDictionary vs reference type
От: karbofos42 Россия  
Дата: 19.02.22 16:49
Оценка:
Здравствуйте, Codealot, Вы писали:

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


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

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

C>Я повторю еще раз, для самых быстрых.


C>Мы говорим о типе, созданном специально для многопоточности. И если в нем простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. А это значит, что generic constraint типа должен в явном виде их запретить. Не доходит?


Я там дополнил своё сообщение примером с "обычным" словарём.
Создали специальный тип для хранения значений по уникальным ключам, но и тут накосячили.
Ничем нельзя пользоваться выходит, нужно всё своё писать правильное.
Re[11]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.02.22 17:21
Оценка:
Здравствуйте, Codealot, Вы писали:

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


K>>https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-5.0

K>>

K>>All public and protected members of ConcurrentDictionary<TKey,TValue> are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentDictionary<TKey,TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.


C>Ты даже не понимаешь о чем идет речь, а туда же.

C>Имеется в виду потокобезопасность только самого объекта ConcurrentDictionary
А что по твоему еще должно быть?
За потокобезопасность объекты ты уже сам должен позаботиться.
Или ты хочешь, что бы внутри методов класса автоматически генерировалиь потокобезопасные конструкции изменения полей, volatile
Ну и стоит почитать Доступ к локальной переменной из разных потоков
Автор: Aquilaware
Дата: 18.11.20
и солнце б утром не вставало, когда бы не было меня
Re[13]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.02.22 17:31
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> А что по твоему еще должно быть?


C>Повторю и для тебя тоже.

C>Мы говорим о типе, созданном специально для многопоточности. И если в нем простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. А это значит, что generic constraint типа должен в явном виде их запретить.

Ну и в чем проблема? Его задача положить достать. А вот за неизменность хэш кода, синхорнизацию данных в объекте и Thread.MemoryBarrier отвечаешь ты.
и солнце б утром не вставало, когда бы не было меня
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 19:30
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Его задача положить достать. А вот за Thread.MemoryBarrier отвечаешь ты.


Звать Thread.MemoryBarrier еще раз, когда он уже вызывается при добавлении — поставить крест на производительности. Так что либо гарантируется, что класс всегда его вызывает, либо это всё не имеет никакого смысла.
Ад пуст, все бесы здесь.
Re[10]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 19:30
Оценка:
Здравствуйте, Teolog, Вы писали:

T>Какого к чертям барьера?


Такого к черту барьера. Иди и читай доки. Начинай отсюда и с прочих азов https://docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked.memorybarrier?view=net-6.0
Ад пуст, все бесы здесь.
Re[2]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 19.02.22 19:35
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Ну или почитай его исходники и убедись, что там всё хорошо.


Вряд ли во всем мире наберется хотя бы тысяча человек, которые реально понимают все эти нюансы.
Ад пуст, все бесы здесь.
Re[17]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 19.02.22 20:22
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Еще раз почитай

S>>volatile

C>Ты когда-нибудь интересовался, сколько он стоит?

Он стоит меньше чем Lock столько же сколько и Interlocked
S>>Ну и Lock то никто не отменял!

C>Отож. Делаем лок при любых операциях с объектом, а ConcurrentDictionary попросту списываем в утиль, потому что он в этом сценарии абсолютно бесполезен.


C>А теперь вопрос на миллион: почему ConcurrentDictionary позволяет работать с объектами, если на самом деле этот юз кейс некорректен?

Еще раз напомни в чем проблема. Я так и не понял.
Все используют и вдруг появился Codealot и выбрасывает в утиль.
Поделись. Тут все кроме тебя не понимают! Объясни на пальцах как должно быть!
Наверное мы тупые
и солнце б утром не вставало, когда бы не было меня
Отредактировано 19.02.2022 20:41 Serginio1 . Предыдущая версия . Еще …
Отредактировано 19.02.2022 20:40 Serginio1 . Предыдущая версия .
Re[7]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.22 17:49
Оценка:
Здравствуйте, Codealot, Вы писали:
C>Тогда такой словарь попросту нельзя использовать с объектами и их использование нужно запретить. Чего не наблюдается.
Вы не могли бы проиллюстрировать это утверждение кодом?
В каком именно сценарии и что сломается?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 20.02.22 18:07
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> А что по твоему еще должно быть?


C>Повторю и для тебя тоже.

C>Мы говорим о типе, созданном специально для многопоточности. И если в нем простейший сценарий положить/извлечь объект опасен, то использовать ConcurrentDictionary с объектами попросту нельзя. А это значит, что generic constraint типа должен в явном виде их запретить.
Неужто и иммутабельные тоже запретить? Почему?
и солнце б утром не вставало, когда бы не было меня
Re[19]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 15:51
Оценка:
Здравствуйте, samius, Вы писали:

S>Т.е. указания о том, почему так делать можно или нельзя, он почему-то пытается найти в документации библиотечных типов, а не в модели памяти.


Объясняю еще раз, для самых сообразительных. Полагаться на модель памяти можно только в том случае, если ConcurrentDictionary гарантированно использует барьер памяти. Ну а ты сам же писал, что полагаться на это нельзя, раз про это не написано в документации. Так что — либо использовать объекты в CD нельзя, либо документацию все же писали кретины.

S>И, видимо, об остальных пользователях дотнета такого же мнения.


Не забудь добавить, что как мы выяснили, ты ничего не знал про memory read-write reordering, но все равно писал многопоточный код.
Ад пуст, все бесы здесь.
Отредактировано 21.02.2022 16:42 Codealot . Предыдущая версия .
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 15:53
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Неужто и иммутабельные тоже запретить? Почему?


При помещении объекта в коллекцию запись данных есть, так что memory read-write reordering. Почему бы тебе не ознакомиться для начала с азами?
Ад пуст, все бесы здесь.
Re[2]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 15:57
Оценка:
Здравствуйте, gusilebedi, Вы писали:

G>Ответ таков — .NET Memory Model гарантирует, что все будет ок, то есть такого порядка выполнения быть не может


Где написано, что она это гарантирует? Потому что я ничего подобного в ней не видел.

G> PS: ваш стиль общения не способствует получению ответов


Не люблю людей, которые ничего не знают но очень веское мнение имеют. Например, никогда даже не слышали про memory read-write reordering или memory barrier, как мы выяснили с парой местных ораторов.
Ад пуст, все бесы здесь.
Re[15]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.02.22 16:09
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Неужто и иммутабельные тоже запретить? Почему?


C>При помещении объекта в коллекцию запись данных есть, так что memory read-write reordering. Почему бы тебе не ознакомиться для начала с азами?

Сам то объект не меняется. Какие проблемы. Те же интернированные строки испокон веков лежат себе в коллекциях. https://docs.microsoft.com/ru-ru/dotnet/api/system.string.intern?view=net-5.0
и солнце б утром не вставало, когда бы не было меня
Re[20]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.02.22 16:15
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Т.е. указания о том, почему так делать можно или нельзя, он почему-то пытается найти в документации библиотечных типов, а не в модели памяти.


C>Объясняю еще раз, для самых сообразительных. Полагаться на модель памяти можно только в том случае, если ConcurrentDictionary гарантированно использует барьер памяти. Ну а ты сам же писал, что полагаться на это нельзя, раз про это не написано в документации. Так что — либо использовать объекты в CD нельзя, либо документацию все же писали кретины.


Ты читал мои ссылки? ConcurrentDictionary для поиска и чтения использует Volatile.Read и lock для цепочки для безопасности записи.
Проблемы с чтением и записью объекта нет, а вот безопасный доступ к свойствам объекта ты уже сам должен позаботиться.
Поэтому у иммутабельных объектов об этом задумываться не надо
и солнце б утром не вставало, когда бы не было меня
Re[8]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 16:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вы не могли бы проиллюстрировать это утверждение кодом?


Я просто исхожу из утверждений предыдущего оратора.

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


Если допустить что это действительно так, то класть объекты в CD нельзя. Нужно объяснять, почему?
Ад пуст, все бесы здесь.
Re[18]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 16:31
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Он стоит меньше чем Lock столько же сколько и Interlocked


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

S>Все используют и вдруг появился Codealot и выбрасывает в утиль.


Тебе напомнить, сколько раз "все" без колебаний делали какую-нибудь хрень? Например, клали CString в %s, если кто-то еще помнит?
Ад пуст, все бесы здесь.
Re[21]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 16:35
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Ты читал мои ссылки? ConcurrentDictionary для поиска и чтения использует Volatile.Read и lock для цепочки для безопасности записи.

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

Это уже совсем другой поворот. Но предыдущий оратор писал, что полагаться на это нельзя.
Ад пуст, все бесы здесь.
Re[16]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 16:35
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Сам то объект не меняется. Какие проблемы.


Меняется, хотя всего один раз.

S>Те же интернированные строки испокон веков лежат себе в коллекциях. https://docs.microsoft.com/ru-ru/dotnet/api/system.string.intern?view=net-5.0


lock-free коллекциях?
Ад пуст, все бесы здесь.
Re[21]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 17:36
Оценка:
Здравствуйте, samius, Вы писали:

S>Вот тут ошибка. Модель памяти — это часть стандарта CLI, на котором построен .NET и C# в частности.


Я вижу, ты мастер переобуться на бегу. Просто процитирую.

Понятно, это на уровне конкретного CPU, и в модели памяти дотнета (и джавы тоже) такого нет.


S>И если мы не можем полагаться на модель памяти, то речь не о .NET, C#, а о какой-то поделке.


Полагаться на модель памяти можно. Но нельзя полагаться на то, чего она не гарантирует.

S>Любой способ передачи ссылки на объект, расположенный в куче .NET обеспечивает видимость корректно инициализированного объекта. За это отвечает реализация EE/JIT.


Ничего подобного она не обеспечивает, и я сам фиксил баги, которые понаделали те кто в это верил. Сами они пофиксить не осилили.

S>Вплоть до размещения барьера памяти внутри конструктора, при необходимости.


А это чертовски интересно. Есть источники?

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


Для любой многопоточности сложнее чем "делаем lock всегда и на все" это — азы.
Ад пуст, все бесы здесь.
Re[19]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.02.22 19:56
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Он стоит меньше чем Lock столько же сколько и Interlocked


C>Можно использовать только в очень ограниченном числе случаев.

C>И если использовать volatile в неизменяемом объекте который ты часто читаешь, то это вусмерть убьет производительность.
Ну а как ты хочешь работать в многопотоке?
Твои решения
S>>Все используют и вдруг появился Codealot и выбрасывает в утиль.

C>Тебе напомнить, сколько раз "все" без колебаний делали какую-нибудь хрень? Например, клали CString в %s, если кто-то еще помнит?

Я не плюсовик. Там свои тараканы. В .Net есть другие примеры. Но интересны твои предложения
и солнце б утром не вставало, когда бы не было меня
Re[17]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.02.22 20:01
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Сам то объект не меняется. Какие проблемы.


C>Меняется, хотя всего один раз.


S>>Те же интернированные строки испокон веков лежат себе в коллекциях. https://docs.microsoft.com/ru-ru/dotnet/api/system.string.intern?view=net-5.0


C>lock-free коллекциях?

Угу и как они защитят от отсутствия Memory Baryer?
Опять же все lock-free основаны на Interlocked, Volatile а он и есть Memory Barrier Lock-free структуры данных. Основы: откуда пошли быть барьеры памяти
и солнце б утром не вставало, когда бы не было меня
Re[17]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.02.22 20:32
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Сам то объект не меняется. Какие проблемы.


C>Меняется, хотя всего один раз.

Он не меняется, а создается 1 раз в конструкторе!
S>>Те же интернированные строки испокон веков лежат себе в коллекциях. https://docs.microsoft.com/ru-ru/dotnet/api/system.string.intern?view=net-5.0

C>lock-free коллекциях?

Там может и SemaphoreSlim
Но если объектов долго занят то там применяют и Lock и Thread.Sleep и Thread.Yield()
SpinWait
public bool NextSpinWillYield
    {
      [__DynamicallyInvokable] get => this.m_count > 10 || PlatformHelper.IsSingleProcessor;
    }

 public void SpinOnce()
    {
      if (this.NextSpinWillYield)
      {
        CdsSyncEtwBCLProvider.Log.SpinWait_NextSpinWillYield();
        int num = this.m_count >= 10 ? this.m_count - 10 : this.m_count;
        if (num % 20 == 19)
          Thread.Sleep(1);
        else if (num % 5 == 4)
          Thread.Sleep(0);
        else
          Thread.Yield();
      }
      else
        Thread.SpinWait(4 << this.m_count);
      this.m_count = this.m_count == int.MaxValue ? 10 : this.m_count + 1;
    }


 public static bool SpinUntil(Func<bool> condition, int millisecondsTimeout)
    {
      if (millisecondsTimeout < -1)
        throw new ArgumentOutOfRangeException(nameof (millisecondsTimeout), (object) millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong"));
      if (condition == null)
        throw new ArgumentNullException(nameof (condition), Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull"));
      uint num = 0;
      if (millisecondsTimeout != 0 && millisecondsTimeout != -1)
        num = TimeoutHelper.GetTime();
      SpinWait spinWait = new SpinWait();
      while (!condition())
      {
        if (millisecondsTimeout == 0)
          return false;
        spinWait.SpinOnce();
        if (millisecondsTimeout != -1 && spinWait.NextSpinWillYield && (long) millisecondsTimeout <= (long) (TimeoutHelper.GetTime() - num))
          return false;
      }
      return true;
    }

https://habr.com/ru/post/459514/
и солнце б утром не вставало, когда бы не было меня
Re[20]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 20:37
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Ну а как ты хочешь работать в многопотоке?

S>Твои решения

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

S> Я не плюсовик. Там свои тараканы. В .Net есть другие примеры. Но интересны твои предложения


Это я просто о том, что "все так делают" вообще не аргумент.
Ад пуст, все бесы здесь.
Re[18]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 20:37
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Опять же все lock-free основаны на Interlocked, Volatile а он и есть Memory Barrier Lock-free структуры данных. Основы: откуда пошли быть барьеры памяти


А предыдущий оратор писал, что нельзя на это полагаться. Так какие претензии ко мне тогда?
Ад пуст, все бесы здесь.
Re[18]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 21.02.22 20:37
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Он не меняется, а создается 1 раз в конструкторе!


Это не отменяет необходимости изменить память, где он будет лежать.
Ад пуст, все бесы здесь.
Re[10]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 01:42
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Да, нужно.


Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.
Ад пуст, все бесы здесь.
Re[22]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.02.22 10:05
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>И если мы не можем полагаться на модель памяти, то речь не о .NET, C#, а о какой-то поделке.


C>Полагаться на модель памяти можно. Но нельзя полагаться на то, чего она не гарантирует.

Разумеется. Но ты ее не читал, потому не знаешь, что она гарантирует, а что — нет.

S>>Любой способ передачи ссылки на объект, расположенный в куче .NET обеспечивает видимость корректно инициализированного объекта. За это отвечает реализация EE/JIT.


C>Ничего подобного она не обеспечивает, и я сам фиксил баги, которые понаделали те кто в это верил. Сами они пофиксить не осилили.


Не знаю, что ты фиксил, но точно не то, о чем говоришь.

S>>Вплоть до размещения барьера памяти внутри конструктора, при необходимости.


C>А это чертовски интересно. Есть источники?

Да. Модель памяти. Открой, почитай.

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


C>Для любой многопоточности сложнее чем "делаем lock всегда и на все" это — азы.


Азы и то, что управляемая память опирается на гарантии о состоянии памяти объекта.
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:26
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Такого не может быть ибо за ссылку и объект отвечает GC. Есть WeakReference но сам то WeakReference существует.


Ты явно не понимаешь, как это всё работает. В том треде, который создает объект — да, там всё есть. В других тредах может быть видно практически любое случайное частичное подмножество из этих данных. Абсолютно никаких гарантий по этому вопросу .NET не дает.
Ад пуст, все бесы здесь.
Отредактировано 22.02.2022 16:27 Codealot . Предыдущая версия .
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Это как так? Архитектура дотнета не даёт возможности получить ссылку без объекта.


Re[12]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 22.02.22
Ад пуст, все бесы здесь.
Re[12]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:27
Оценка:
Здравствуйте, samius, Вы писали:

S>Тут не ConcurrentDictionary надо запрещать, а сами объекты. Даже GC не сможет переварить такие звезды.


Re[12]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 22.02.22
Ад пуст, все бесы здесь.
Re[23]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:31
Оценка:
Здравствуйте, samius, Вы писали:

S>Разумеется. Но ты ее не читал, потому не знаешь, что она гарантирует, а что — нет.


Читал и знаю, в отличие от тебя.

S>Не знаю, что ты фиксил, но точно не то, о чем говоришь.


Прорицатели в треде.

S>Да. Модель памяти. Открой, почитай.


Там ничего подобного нет. Давай конкретную цитату, если она по твоему там есть. Особенно про барьер памяти в конструкторе. Оччень интересно, где ты такие чудеса вычитал.

S>Азы и то, что управляемая память опирается на гарантии о состоянии памяти объекта.


Re[12]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 22.02.22
Ад пуст, все бесы здесь.
Re[22]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:32
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Нужно. Это касается только коллекции, но не объектов.

S>За доступ к свойствам объектов отвечаешь ты

Барьер памяти касается всего.

C>Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.
Такого не может быть ибо за ссылку и объект отвечает GC.

ты уж определись, а то у тебя семь пятниц на неделе
Ад пуст, все бесы здесь.
Re[20]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:33
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Нельзя полагаться на что? За доступ к свойствам объекта из многопотока отвечаешь ты


На то, что там барьер памяти. Так можно полагаться на то что он там есть или нет?
Ад пуст, все бесы здесь.
Re[20]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 16:35
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Нет не надо. Ибо свойства то не меняются и по барабану где лежит ссылка и объектные свойства в регистре, кэше или памяти!


Пц. Неважно, меняются свойства или нет. Память — меняется. Это единственное, что должно тебя заботить в этом контексте.
Ад пуст, все бесы здесь.
Re[23]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 17:23
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Нужно. Это касается только коллекции, но не объектов.

S>>За доступ к свойствам объектов отвечаешь ты

C>Барьер памяти касается всего.

https://habr.com/ru/post/130318/

Для решения данной проблемы существует универсальный метод — добавление барьера памяти(memory barrier, memory fence).
Существует несколько видов барьеров памяти: полный, release fence и accure fence.
Полный барьер гарантирует, что все чтения и записи расположенные до/после барьера будут выполнены так же до/после барьера, то есть никакая инструкция обращения к памяти не может перепрыгнуть барьер.
Теперь разберемся с двумя другими видами барьеров:
Accure fence гарантирует что инструкции, стоящие после барьера, не будут перемещены в позицию до барьера.
Release fence гарантирует, что инструкции, стоящие до барьера, не будут перемещены в позицию после барьера.
Еще пару слов о терминологии. Термин volatile write означает выполнение записи в память в сочетании с созданием release fence. Термин volatile read означает чтение памяти в сочетании с созданием accure fence.

.NET предоставляет следующие методы работы с барьерами памяти:
метод Thread.MemoryBarrier() создает полный барьер памяти
ключевое слово volatile превращает каждую операцию над переменной, помеченной этим словом в volatile write или volatile read соответсвенно.
метод Thread.VolatileRead() выполняет volatile read
метод Thread.VolatileWrite() выполняет volatile write






Методы Thread.VolatileWrite и Thread.VolatileRead
Ознакомимся с методами Thread.VolatileWrite и Thread.VolatileRead более подробно.
В MSDN о VolatileWrite написанно: “Записывает значение непосредственно в поле, так что оно становится видимым для всех процессоров компьютера.”
На самом деле это описание не совсем корректно. Эти методы гарантируют две вещи: отсутствие оптимизаций компилятора1 и отсутствие перестановок инструкций в соответствии с свойставми volatile read или write. Строго говоря метод VolatileWrite не гарантирует, что значение немедленно станет видимым для других процессоров, а метод VolatileRead не гарантирует, что значение не будет прочитанно из кеша2. Но в силу отсутствия оптимизаций кода компилятором и когерентности кэшей процессора мы можем считать что описание из MSDN корректно.


Опасности volatile

Взглянув на реализацию методов VolatileWrite и VolatileRead становится понятно, что вот такая пара инструкций может быть переставлена:
Thread.VolatileWrite(b)
Thread.VolatileRead(a)

Так как это поведение заложено в определении терминов volatile read и write то это не является багом и аналогичным поведением обладают операции с переменными, помеченными ключевым словом volatile.


Производительность Thread.Volatile* и ключевого слово volatile

На большинстве платформ (точнее говоря, на всех платформах, поддерживаемых Windows, кроме умирающей IA64) все записи и чтения являются volatile write и volatile read соответственно. Таким образом, во время выполнения ключевое слово volatile не оказывает никакого влияния на производительность. Напротив, методы Thread.Volatile*, во-первых, несут накладные расходы на сам вызов метода, помеченный как MethodImplOptions.NoInlining, и, во-вторых, в текущей реализации, создают полный барьер памяти. То есть, с точки зрения производительности, в большинстве случаев предпочтительнее использование ключевого слова.


https://ru.wikipedia.org/wiki/%D0%91%D0%B0%D1%80%D1%8C%D0%B5%D1%80_%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8

Барьер памяти (англ. memory barrier, membar, memory fence, fence instruction) — вид барьерной инструкции, которая приказывает компилятору (при генерации инструкций) и центральному процессору (при исполнении инструкций) устанавливать строгую последовательность между обращениями к памяти до и после барьера. Это означает, что все обращения к памяти перед барьером будут гарантированно выполнены до первого обращения к памяти после барьера.

Барьеры памяти необходимы, так как большинство современных процессоров используют оптимизации производительности, которые могут привести к переупорядочиванию инструкций. Также переупорядочивание обращений к памяти может быть вызвано компилятором в процессе оптимизации использования регистров целевого процессора. Такие перестановки обычно не влияют на корректность программы с одним потоком исполнения, но могут вызвать непредсказуемое поведение в многопоточных программах. Правила изменения порядка исполнения инструкций зависят от архитектуры. Некоторые архитектуры предоставляют несколько типов барьеров с различными гарантиями. Например, amd64 предоставляет следующие инструкции: SFENCE (англ. store fence), LFENCE(англ. load fence), MFENCE(англ. memory fence)[1]. Intel Itanium обеспечивает отдельные «запоминающие» (англ. acquire) и «освобождающие» (англ. release) барьеры памяти, которые учитывают видимость операций чтения после записи с точки зрения читателя и писателя соответственно.



Оптимизации порядка исполнения компилятором
Барьеры памяти работают только на аппаратном уровне. Компиляторы могут также переупорядочить инструкции как часть оптимизации программы. Меры по предотвращению переупорядочивания необходимы только для данных, которые не защищены примитивами синхронизации.

В языках С и С++ ключевое слово volatile предназначено для исключения оптимизаций компилятора. Используется чаще всего для работы с отображаемым в память вводом-выводом. Однако данное ключевое слово (в отличие от Java) никак не обеспечивает атомарность и защиту от внеочередного исполнения.[3]


C>

C>>Ну например, так звезды сложились что у тебя есть ссылка и нет самого объекта.
C>Такого не может быть ибо за ссылку и объект отвечает GC.

C>ты уж определись, а то у тебя семь пятниц на неделе
Ты про что? Объясни как у тебя могут звезды сложиться?
и солнце б утром не вставало, когда бы не было меня
Re[21]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 17:33
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Нет не надо. Ибо свойства то не меняются и по барабану где лежит ссылка и объектные свойства в регистре, кэше или памяти!


C>Пц. Неважно, меняются свойства или нет. Память — меняется. Это единственное, что должно тебя заботить в этом контексте.

И что меняется для иммутабельного объекта?
и солнце б утром не вставало, когда бы не было меня
Re[24]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 17:39
Оценка:
Здравствуйте, Serginio1, Вы писали:

Цитировать простыни я тоже умею, но не занимаюсь таким тупизмом. Так вот — Interlocked.MemoryBarrier это full fence, раз ты не в курсе. Если он вызывается, то синхронизирует всю память для процессора текущего треда.

S> Ты про что? Объясни как у тебя могут звезды сложиться?


Ад пуст, все бесы здесь.
Re[15]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 18:23
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>GC при сборке мусора останавливает потоки.


C>Да. Останаваливает и синхронизирует всё что можно, прежде чем собирать мусор. Я надеюсь ты не думаешь, что при создании каждого объекта тоже синхронизируется кэш всех процессоров, к примеру?

Ты читал про Когерентность кэша
Зачем я тебе ссылки то даю?
При создании объекта в конструкторе другие потоки его не читают ибо ссылки то нет. А вот если после создания какое то поле поместили в регистр, то вот здесь и надо предусмотреть эту ситуацию.
и солнце б утром не вставало, когда бы не было меня
Re[25]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 18:31
Оценка:
Здравствуйте, Codealot, Вы писали:

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


C>Цитировать простыни я тоже умею, но не занимаюсь таким тупизмом. Так вот — Interlocked.MemoryBarrier это full fence, раз ты не в курсе. Если он вызывается, то синхронизирует всю память для процессора текущего треда.


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



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


Вот процессор не будет запихивать данные в регистр только в области видимости кода. В другом методе уже скомпилирован код где поле закинуто в регистр и там прекрасно меняется.
Как Interlocked.MemoryBarrier в другом методе может на это повлиять?
и солнце б утром не вставало, когда бы не было меня
Re[23]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 18:36
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Как я тебе привел ниже есть несколько типов барьеров памяти. Основной это компилятор. Что бы не засунули переменную в регистр.


C>Компилятор дает какие-либо гарантии только для текущего треда.

Еще раз как мемори барьер в одном потоке влияет на использование поля в регистре в другом методе в котором не используется мемори барьер?

S>>Как уже тут отмечали когерентность кэшей процессора достаточно и отсутствия оптимизаций кода компилятором.


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


Это как? Конструктор это как раз гарантирует. А вот если ты его изменяешь после конструктора то это уже твои проблемы!
и солнце б утром не вставало, когда бы не было меня
Re[23]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.02.22 18:39
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> И что меняется для иммутабельного объекта?


C>Я с тебя просто офигеваю. Ты вообще в курсе, что объект и ссылку(и) на него надо записать в память, когда он создается?

Это я с тебя охреневаю. Конструктр отдает ссылку после того как создан и заполен (инициализирован)объект
Ссылка получается после выделения и заполнения памяти, но никак по другому. Если же ты заполняешь после конструктора без синхронизации то это уже твои проблемы!
и солнце б утром не вставало, когда бы не было меня
Re[26]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 22.02.22 21:00
Оценка:
Здравствуйте, Serginio1, Вы писали:

И охота тебе ветки плодить. Соберу все сюда.

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

S>Как Interlocked.MemoryBarrier в другом методе может на это повлиять?

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

S>Это как? Конструктор это как раз гарантирует.


Приведи цитату, где написано что что-то в этом духе гарантируется для всех тредов, а не только для текущего.

S> Это я с тебя охреневаю. Конструктр отдает ссылку после того как создан и заполен (инициализирован)объект


Сначала выделяется память и сохраняется ссылка на нее, потом заполняются поля объекта.

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


Тебе известны какие-то спеки, которые говорят о таких гарантиях для всех тредов?
Мне известны спеки, которые говорят о прямо противоположном.

https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/december/csharp-the-csharp-memory-model-in-theory-and-practice

One source of complexity in multithreaded programming is that the compiler and the hardware can subtly transform a program’s memory operations in ways that don’t affect the single-threaded behavior, but might affect the multithreaded behavior. Consider the following method:
XML

void Init() {
_data = 42;
_initialized = true;
}

If _data and _initialized are ordinary (that is, non-volatile) fields, the compiler and the processor are allowed to reorder the operations so that Init executes as if it were written like this:
XML

void Init() {
_initialized = true;
_data = 42;
}

The C# memory model permits reordering of memory operations in a method, as long as the behavior of single-threaded execution doesn’t change.


S>При создании объекта в конструкторе другие потоки его не читают ибо ссылки то нет. А вот если после создания какое то поле поместили в регистр, то вот здесь и надо предусмотреть эту ситуацию.


После создания объекта, нет никаких гарантий что другие треды получат и прочитают данные в том же порядке, как они были записаны. И даже что они были записаны в память в том же порядке, как ты написал в коде — тоже на самом деле не гарантируется.
Ад пуст, все бесы здесь.
Re[15]: ConcurrentDictionary vs reference type
От: Sinclair Россия https://github.com/evilguest/
Дата: 23.02.22 05:37
Оценка:
Здравствуйте, Codealot, Вы писали:

C>Для человека который никогда не слышал про memory read/write reordering у тебя слишком много понтов

Наверное, это оттого, что я не только слышал про memory read/write reordering, но и понимаю, как его обрабатывает CLR
А вы всё же приведите пример .Net кода, который, по-вашему, небезопасен.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 23.02.22 16:04
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Наверное, это оттого, что я не только слышал про memory read/write reordering, но и понимаю, как его обрабатывает CLR


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

S>А вы всё же приведите пример .Net кода, который, по-вашему, небезопасен.


Любой, в котором ты создаешь объект в одном треде, читаешь в другом и не делаешь никаких добавочных действий, чтобы избавиться от реордеринга.
Ад пуст, все бесы здесь.
Re[14]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 23.02.22 17:19
Оценка:
Здравствуйте, xpalex, Вы писали:

X>

X>All memory allocated for static variables


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

X>

X>or for converting errors due to
X>lack of synchronization into non-fatal, non-corrupting, user-visible exceptions.


Бинго. Прохода по памяти не будет, а вот исключение из-за обращения к некорректным (с точки зрения твоего кода) данным ты вполне можешь словить. null ref, например.
Ад пуст, все бесы здесь.
Re[15]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 24.02.22 01:29
Оценка:
Здравствуйте, Codealot, Вы писали:

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


X>>
X>>All memory allocated for static variables
X>>

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


for static variables (other than those assigned RVAs within a PE file, see
Partition II) and objects...


Надо прокачивать скил чтения.

X>>
X>>or for converting errors due to
X>>lack of synchronization into non-fatal, non-corrupting, user-visible exceptions.
X>>

C>Бинго. Прохода по памяти не будет, а вот исключение из-за обращения к некорректным (с точки зрения твоего кода) данным ты вполне можешь словить. null ref, например.

Не смог понять что там написано? Если целевая платформа не умеет в синхронизацию (в том числе памяти через memory fences), то кидать исключение.
Если твоя целевая платформа не умеет, то зачем ты выносишь мозг мемори барьерами?

Ну и если интересно как там устроено, можн смотреть здесь: https://github.com/mono/mono/blob/main/mono/sgen/sgen-gc.c
Отредактировано 24.02.2022 3:34 xpalex . Предыдущая версия .
Re[4]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 24.02.22 22:38
Оценка:
Здравствуйте, Ночной Смотрящий, Вы писали:

НС>А то прям точное описание твоего поведения здесь.


Врёшь ведь и не краснеешь.
Ад пуст, все бесы здесь.
Re[31]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.02.22 07:24
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Барьер ты как раз и вызываешь, что бы компилятор не оптимизировал. Но volatile этим и занимется без полного барьера


C>А также для того, чтобы синхронизировать память. Если они вызываются.


S>> Иии. Это чему мешает например для имутабельных классов?


C>Всему.

Ну ты хоть пример то приведи! А то все болтовня!
S>> Не непонятно. ССылка когда становится доступной? Только после создания и инициализации в конструкторе. Если ты конечно из контруктора её не передаешт. Но это твои проблемы

C>Нет никакого "после создания", Нео.

угу ссылка=new Конструктор() откуда она видна из другого процесса, а вот куда в итоге это ссылка записывается там и нужна синхронизация
S>> Ну вот строки прекрасно работают и нет никаких проблем с потоками.

C>Где конкретно, в каком сценарии и что это доказывает?

Конкретно интернированные строки! String.Intern делает строки ещё интереснее
Подводные камни в бассейне строк, или ещё один повод подумать перед интернированием экземпляров класса String в C#

Это по сути то и есть ConcurrentDictionary. Но никаких проблем с интернированными строками я не слышал!
А string то это объект!
и солнце б утром не вставало, когда бы не было меня
Отредактировано 25.02.2022 7:26 Serginio1 . Предыдущая версия .
Re[33]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 27.02.22 17:26
Оценка:
Здравствуйте, Codealot, Вы писали:

C>Там совершенно определенно делается лок при каждом вызове String.Intern, о чем и написано в твоей же ссылке. Ты читать вообще умеешь?

Так ConcurrentDictionary так же делается Lock только на цепоку с одинаковым hash % размер таблицы. Я же тебе код приводил, а ты смеялся.
http://rsdn.org/forum/dotnet/8199139.1
Автор: Serginio1
Дата: 19.02.22

Проблема как ты тут говорил не в ConcurrentDictionary, а в выделении памяти под строку.
Так вот проблем с конструктором строки нет!
Ты же говоришь, что нельзя применять ConcurrentDictionary с ссылочными типами, однако String.Intern прекрасно используют!
и солнце б утром не вставало, когда бы не было меня
Отредактировано 27.02.2022 18:02 Serginio1 . Предыдущая версия .
Re[14]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 10:16
Оценка:
Здравствуйте, xpalex, Вы писали:

X>Т.е. до спеки ты не добрался?


X>https://www.ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf


X>

X>I.12.6.8 Other memory model issues
X>All memory allocated for static variables (other than those assigned RVAs within a PE file, see
X>Partition II) and objects shall be zeroed before they are made visible to any user code.
X>A conforming implementation of the CLI shall ensure that, even in a multi-threaded environment
X>and without proper user synchronization, objects are allocated in a manner that prevents
X>unauthorized memory access and prevents invalid operations from occurring. In particular, on
X>multiprocessor memory systems where explicit synchronization is required to ensure that all
X>relevant data structures are visible (for example, vtable pointers) the Execution Engine shall be
X>responsible for either enforcing this synchronization automatically or for converting errors due to
X>lack of synchronization into non-fatal, non-corrupting, user-visible exceptions.
X>It is explicitly not a requirement that a conforming implementation of the CLI guarantee that all
X>state updates performed within a constructor be uniformly visible before the constructor
X>completes. CIL generators can ensure this requirement themselves by inserting appropriate calls
X>to the memory barrier or volatile write instructions.

Бинго! Т.е. Codealot таки прав. Тут написано, что гарантируется только аллокация участка памяти и зануление, но не инициализация. А выполнение конструктора может быть и не visible.

В java есть такой тонкий момент, решается final-полями (или readonly в терминологии дотнета) и он явно описан в стандарте языка.
Вот в таком коде:
class FinalFieldExample { 
    final int x;
    int y; 

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    }

Значение y может быть 0 или 4 из другого потока, т.к. оно не final. А для x — гарантируется, что будет 3. Иными словами final создаёт барьер в компиляторе (т.е. без лишнего перформанс-проседания). Как такое решается в дотнете неясно. Судья по обсуждению тут — пока что лишь добавлением недокументированных барьеров во всякие рандомные места.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 10:35
Оценка:
Здравствуйте, ·, Вы писали:

·>Бинго! Т.е. Codealot таки прав. Тут написано, что гарантируется только аллокация участка памяти и зануление, но не инициализация. А выполнение конструктора может быть и не visible.


Все-таки тут написано что
1) за обнуление отвечает непосредственно CLI.
2) То, что будет видно все состояние объекта, выполненное в конструкторе — не является требованием непосредственно к CLI, т.к. это требование могут обеспечивать CIL генераторы.
Re[16]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 11:03
Оценка:
Здравствуйте, samius, Вы писали:

S>·>Бинго! Т.е. Codealot таки прав. Тут написано, что гарантируется только аллокация участка памяти и зануление, но не инициализация. А выполнение конструктора может быть и не visible.

S>Все-таки тут написано что
S>1) за обнуление отвечает непосредственно CLI.
+1. Только это не относится к вопросу.

S>2) То, что будет видно все состояние объекта, выполненное в конструкторе — не является требованием непосредственно к CLI, т.к. это требование могут обеспечивать CIL генераторы.

Эээ.. Не очень ясно. Тут написано "can", т.е. какой-то кодогенератор _может_ вставлять барьеры, видимо в зависимости от семантики самого ЯП и данного куска кода. Но ведь может-то и не вставлять! Т.е. эта цитата вообще нерелевантна к оригинальному вопросу, никак на него не отвечает.
Хочется увидеть ссылку на спеку, что c# генератор таки обязуется делать операции в конструкторе visible другим тредам. Ещё интересен такой кусок кода:
class Example {
  int value;
  
  public Example(IObserver observer) {
    this.value = 42;
    observer.sendToAnotherThread(this);// вот тут другой тред точно увидит 42 или может быть и 0??
  }
}


В случае java и final поля — гарантия есть по стандарту.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[17]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 11:23
Оценка:
Здравствуйте, ·, Вы писали:

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


S>>2) То, что будет видно все состояние объекта, выполненное в конструкторе — не является требованием непосредственно к CLI, т.к. это требование могут обеспечивать CIL генераторы.

·>Эээ.. Не очень ясно. Тут написано "can", т.е. какой-то кодогенератор _может_ вставлять барьеры, видимо в зависимости от семантики самого ЯП и данного куска кода. Но ведь может-то и не вставлять! Т.е. эта цитата вообще нерелевантна к оригинальному вопросу, никак на него не отвечает.
·>Хочется увидеть ссылку на спеку, что c# генератор таки обязуется делать операции в конструкторе visible другим тредам. Ещё интересен такой кусок кода:
Разумеется, CIL генератор не обязан это делать, если с этим справляется CLI или железо этого не требует. Эта спека все-таки про модель памяти, а не про CIL генератор.
·>
·>class Example {
·>  int value;
  
·>  public Example(IObserver observer) {
·>    this.value = 42;
·>    observer.sendToAnotherThread(this);// вот тут другой тред точно увидит 42 или может быть и 0??
·>  }
·>}
·>

Из данной цитаты вообще ничего не следует в отношении данного примера. Т.е. конструктор еще не отработал формально и мы не должны делать выводов о видимости состояния кроме как о том, что обнулено оно будет железно. Но sendToAnotherThread не имеет магического способа заставить другой поток работать с этими данными по ссылке. Или этой магии придется обращаться уже к неким функциям управления потоками, а уж они обеспечат видимость измененного в конструкторе состояния другим потокам. В любом случае this придется опубликовать.
Re[18]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 12:25
Оценка:
Здравствуйте, samius, Вы писали:

S>>>2) То, что будет видно все состояние объекта, выполненное в конструкторе — не является требованием непосредственно к CLI, т.к. это требование могут обеспечивать CIL генераторы.

S>·>Эээ.. Не очень ясно. Тут написано "can", т.е. какой-то кодогенератор _может_ вставлять барьеры, видимо в зависимости от семантики самого ЯП и данного куска кода. Но ведь может-то и не вставлять! Т.е. эта цитата вообще нерелевантна к оригинальному вопросу, никак на него не отвечает.
S>·>Хочется увидеть ссылку на спеку, что c# генератор таки обязуется делать операции в конструкторе visible другим тредам. Ещё интересен такой кусок кода:
S>Разумеется, CIL генератор не обязан это делать, если с этим справляется CLI или железо этого не требует. Эта спека все-таки про модель памяти, а не про CIL генератор.
Оригинальный вопрос был именно про генератор c#. Причём тут CLI и обнуление памяти — неясно. Зачем привели эту цитату — неясно.

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

Ок. Хорошо, но уже лучше. А вот такой пример тоже работает так же? Если он работает как-то иначе, плиз ссылку в стандарт.

class Example {
  int value;
  
  public Example() {
    this.value = 42;
  }
}
...
observer.sendToAnotherThread(new Example());// вот тут другой тред точно увидит 42 или может быть и 0??


S>Но sendToAnotherThread не имеет магического способа заставить другой поток работать с этими данными по ссылке. Или этой магии придется обращаться уже к неким функциям управления потоками, а уж они обеспечат видимость измененного в конструкторе состояния другим потокам. В любом случае this придется опубликовать.

_заставить_ одно дело. Другое дело, что ссылка может быть передана и без _заставить_ (например через запись-чтение non-volatile переменной), и другой тред неожиданно увидит 0. А _заставление_ произойдёт немного позже явным барьером, например. Иными словами, барьер обязует лишь то, что изменение будет увиденно, но никто не гарантирует, что если барьера (ещё) нет, то и никто не увидит.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[20]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 13:23
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Разумеется, CIL генератор не обязан это делать, если с этим справляется CLI или железо этого не требует. Эта спека все-таки про модель памяти, а не про CIL генератор.

S>·>Оригинальный вопрос был именно про генератор c#. Причём тут CLI и обнуление памяти — неясно. Зачем привели эту цитату — неясно.
S>Оригинальный вопрос был про то, нужен ли бубен при работе с ConcurrentDictionary. Генератор там ну никак не упоминался, если, конечно он не назван бубном. Но вряд ли.
ConcurrentDictionary это лишь один из способов передачи объектов между тредами.

S>я это вижу таким образом, что либо conforming implementation of the CLI, либо CIL generators должны нам обеспечить 42.

А я не вижу. CLI должен обеспечить только 0 (он отвечает за zeroeing и всё). А про CIL написано, что лишь _может_, "должен" не написано, как ты заявляешь. Подтверди свою т.з. документацией.

S>Так причем тут словарь вообще и документация к нему? Даже если мы руками выполним барьер памяти, это не приведет к принудительному завершению конструктора в другом треде и заполнению полей объекта.

Э.. Ну да. Вопрос в другом. Вопрос в том, что в спеке c# делает конструкторы чем-то особенным? Чем sendToAnotherThread внутри конструктора отличается от sendToAnotherThread снаружи конструктора?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[21]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 14:11
Оценка:
Здравствуйте, ·, Вы писали:

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


S>>Оригинальный вопрос был про то, нужен ли бубен при работе с ConcurrentDictionary. Генератор там ну никак не упоминался, если, конечно он не назван бубном. Но вряд ли.

·>ConcurrentDictionary это лишь один из способов передачи объектов между тредами.
не поспоришь

S>>я это вижу таким образом, что либо conforming implementation of the CLI, либо CIL generators должны нам обеспечить 42.

·>А я не вижу. CLI должен обеспечить только 0 (он отвечает за zeroeing и всё). А про CIL написано, что лишь _может_, "должен" не написано, как ты заявляешь. Подтверди свою т.з. документацией.
Верно, и про CIL написано что может. То, что он должен — не написано, т.к. может и CLI за это отвечать.

S>>Так причем тут словарь вообще и документация к нему? Даже если мы руками выполним барьер памяти, это не приведет к принудительному завершению конструктора в другом треде и заполнению полей объекта.

·>Э.. Ну да. Вопрос в другом. Вопрос в том, что в спеке c# делает конструкторы чем-то особенным? Чем sendToAnotherThread внутри конструктора отличается от sendToAnotherThread снаружи конструктора?
C#, на сколько я помню, тут вообще не при чем. И если даже мы найдем в спеке языка что-то, то вопрос о том, будет ли это исполняться для VB.NET или F#, или еще какого-то дотнет языка — останется открыт. А ConcurrentDictionary он вне языка. CLI и CIL, собственно, тоже. Поэтому, я бы C# в эту тему не впутывал бы.
Re[22]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 14:30
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Оригинальный вопрос был про то, нужен ли бубен при работе с ConcurrentDictionary. Генератор там ну никак не упоминался, если, конечно он не назван бубном. Но вряд ли.

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

S>>>я это вижу таким образом, что либо conforming implementation of the CLI, либо CIL generators должны нам обеспечить 42.

S>·>А я не вижу. CLI должен обеспечить только 0 (он отвечает за zeroeing и всё). А про CIL написано, что лишь _может_, "должен" не написано, как ты заявляешь. Подтверди свою т.з. документацией.
S>Верно, и про CIL написано что может. То, что он должен — не написано, т.к. может и CLI за это отвечать.
Да ё-моё, ты всё смешал. Про CIL написано о конструкторах, а про CLI написано о zeroeing. Т.е. из цитаты следует, что 0 будет точно (т.е. не будет случайных значений из неинициалированного куска памяти как бывает в Плюсах, например), но гарантии что будет 42 я в упор не вижу.

S>>>Так причем тут словарь вообще и документация к нему? Даже если мы руками выполним барьер памяти, это не приведет к принудительному завершению конструктора в другом треде и заполнению полей объекта.

S>·>Э.. Ну да. Вопрос в другом. Вопрос в том, что в спеке c# делает конструкторы чем-то особенным? Чем sendToAnotherThread внутри конструктора отличается от sendToAnotherThread снаружи конструктора?
S>C#, на сколько я помню, тут вообще не при чем. И если даже мы найдем в спеке языка что-то, то вопрос о том, будет ли это исполняться для VB.NET или F#, или еще какого-то дотнет языка — останется открыт.
Не смешивай consructors и zeroeing. C# тут притом что он обеспечивает конструкторы. А CIL обеспечивает аллокацию памяти и инициализацию её нулями.

S>А ConcurrentDictionary он вне языка. CLI и CIL, собственно, тоже. Поэтому, я бы C# в эту тему не впутывал бы.

Да, конкретно ConcurrentDictionary тут и не причём, он тут лишь как средство межтредной передачи. Да и не относится он к c#, он относится к стандартной библиотеке, а не к языку.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 28.02.2022 14:31 · . Предыдущая версия .
Re[23]: ConcurrentDictionary vs reference type
От: samius Япония http://sams-tricks.blogspot.com
Дата: 28.02.22 14:50
Оценка:
Здравствуйте, ·, Вы писали:

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


S>>не поспоришь

·>Плюс идея в том, что наличие барьеров в текущей имплементации ConcurrentDictionary может маскировать проблему. Что-нибудь может измениться внезапно (оставаясь в рамках документированного контракта ConcurrentDictionary), и полезут интересные баги.
И это не проблема ConcurrentDictionary и его контракта, т.к. у нас есть несколько Concurrent* классов, да и не только они.

·>Да ё-моё, ты всё смешал. Про CIL написано о конструкторах, а про CLI написано о zeroeing. Т.е. из цитаты следует, что 0 будет точно (т.е. не будет случайных значений из неинициалированного куска памяти как бывает в Плюсах, например), но гарантии что будет 42 я в упор не вижу.

Так в описании CLI тебе эту гарантию и не дают, но указывают, что этим могут заниматься сущности за пределом ответственности CLI. При необходимости, разумеется.

S>>C#, на сколько я помню, тут вообще не при чем. И если даже мы найдем в спеке языка что-то, то вопрос о том, будет ли это исполняться для VB.NET или F#, или еще какого-то дотнет языка — останется открыт.

·>Не смешивай consructors и zeroeing. C# тут притом что он обеспечивает конструкторы. А CIL обеспечивает аллокацию памяти и инициализацию её нулями.
C# тут лишний, т.к. конструкторы бывают и без C#. В C++/CLI или даже просто в IL тоже есть конструкторы.

S>>А ConcurrentDictionary он вне языка. CLI и CIL, собственно, тоже. Поэтому, я бы C# в эту тему не впутывал бы.

·>Да, конкретно ConcurrentDictionary тут и не причём, он тут лишь как средство межтредной передачи. Да и не относится он к c#, он относится к стандартной библиотеке, а не к языку.
Re[26]: ConcurrentDictionary vs reference type
От: · Великобритания  
Дата: 28.02.22 18:48
Оценка:
Здравствуйте, samius, Вы писали:

S>·>Перечитай что я написал. Я ничего не говорил о том, что в ConcurrentDictionary есть проблема. Я говорил, что проблема может быть в коде, который использует ConcurrentDictionary, но эта проблема не видна, т.к. замаскирована барьерами в ConcurrentDictionary. Т.е. присваивание в конструкторе будет работать правильно только с конкретной имплементацией платформы.

S>Извини, но мы тут про корректную имплементацию платформы и говорим. Если тебе нужна некорректная имплементация, то ты ошибся форумом, кмк. Не хотел обидеть, если что.
Я написал "конкретную", а ты читаешь "корректную". Перечитай, что я пишу. А теперь ещё раз внимательно, прежде чем в очередной раз ответить невпопад.

.>>Собственно, so тоже согласен, что это implementation defined:

S>·>

It is not specified in ECMA standard, it is just the microsoft implementation of the CLR gives this guarantee. If you run this code in either CLR 1.0 or any other implementation of CLR, your code is likely to break.

S>·>Собственно, как я понял, Codealot это и имел в виду.
S>Ну так в рамках CLR 1.0 ему придется отказаться от ConcurrentDictionary вобоще. Не заведется, т.к. там нужен 2.0 минимум, хотя бы потому, что генерик. И с любой other implementation or CLR следовало бы уточнить для начала это, а не называть всех кретинами.
Стиль его общения, конечно, не очень, но это не отменяет проблему.

S>·>И сделано это ценой потери перформанса:

S>·>

unfortunate that synchronization cost caused by this CLR2.0 guarantee is payed by all code, yet typically only <1% of code deals with threading at all.

S>и много ли потеряли?
Это другой вопрос. Я не знаю. Думаю, от аппаратной платформы зависит.

S>>>Так в описании CLI тебе эту гарантию и не дают, но указывают, что этим могут заниматься сущности за пределом ответственности CLI. При необходимости, разумеется.

S>·>Твоё заявление: "CIL generators должны нам обеспечить 42", неверно, это ошибка. Ничего в стандарте 42 не обеспечивает, только текущие детали имплементации платформы от microsoft это обещают.
S>Не ничего не обеспечивает, а не назван конкретный механизм, обеспечивающий это.
Я вообще не вижу, что там названы вообще хоть какие-то конкретные механизмы, этого в стандарте и не должно быть. Главное то, что в стандарте не написано, что этот механизм должен существовать.

S>>>·>Не смешивай consructors и zeroeing. C# тут притом что он обеспечивает конструкторы. А CIL обеспечивает аллокацию памяти и инициализацию её нулями.

S>>>C# тут лишний, т.к. конструкторы бывают и без C#. В C++/CLI или даже просто в IL тоже есть конструкторы.
S>·>Ну и? Кто из них должен обеспечить 42 по-твоему?!
S>Точно не уровень языка. CLI или CIL генератор.
Может я точно не знаю, но как я понимаю, CIL generator это такая штука, которая из c# генерит IL-инструкции. И то какие инструкции будут сгенерированы (в данном случае нас интересуют барьеры), определяются исходным кодом и семантикой c#.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[34]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 07.03.22 19:51
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Так ConcurrentDictionary так же делается Lock только на цепоку с одинаковым hash % размер таблицы. Я же тебе код приводил, а ты смеялся.


Если делать lock на каждую операцию с коллекцией, то производительность будет пробивать днище. Так что, очевидно, там это не делается.

S> Ты же говоришь, что нельзя применять ConcurrentDictionary с ссылочными типами, однако String.Intern прекрасно используют!


Нет, я ничего подобного не говорил. Если у ConcurrentDictionary есть гарантии по наличию барьера памяти при добавлении объектов, то проблем с неизменяемыми объектами нет. А вот если бы таких гарантий не было, как заявлял
Автор: samius
Дата: 19.02.22
samius, то проблемы несомненно были бы.
Ад пуст, все бесы здесь.
Re[34]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 07.03.22 19:51
Оценка:
Здравствуйте, xpalex, Вы писали:

X>Но если открыть описание memory model clr 2.0, то там явно указано, что reordering STORE,STORE запрещен. Т.е. рантайм берет на себя отвественность по генерации соотвествующего нативного кода при jit-е


А LOAD, LOAD разрешен. Как и LOAD, STORE. Так что без разницы.
Вот тут написано вполне буквально:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2

As an example, consider this method:
XML

void Init() {
_data = 42;
_initialized = true;
}

If _data and _initialized are ordinary (that is, non-volatile) fields, the compiler and the processor are allowed to reorder the operations so that Init executes as if it were written like this:
XML

void Init() {
_initialized = true;
_data = 42;
}


X>Поэтому ты сначала сделай proof-of-concept, который сможет прочитать неиницилизировнное поле сконструированного объекта


Если бы ты хоть немного понимал о чем говоришь, то знал бы, насколько трудно воспроизводить подобные баги.
Ад пуст, все бесы здесь.
Re[35]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 07.03.22 20:30
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Так ConcurrentDictionary так же делается Lock только на цепоку с одинаковым hash % размер таблицы. Я же тебе код приводил, а ты смеялся.


C>Если делать lock на каждую операцию с коллекцией, то производительность будет пробивать днище. Так что, очевидно, там это не делается.

Ты не читатель. Сказано, же что блокировка не на всю таблицу, а только на hash % размер таблицы.
Посмотри исходники.
S>> Ты же говоришь, что нельзя применять ConcurrentDictionary с ссылочными типами, однако String.Intern прекрасно используют!

C>Нет, я ничего подобного не говорил. Если у ConcurrentDictionary есть гарантии по наличию барьера памяти при добавлении объектов, то проблем с неизменяемыми объектами нет. А вот если бы таких гарантий не было, как заявлял
Автор: samius
Дата: 19.02.22
samius, то проблемы несомненно были бы.


Там проблема не в барьере памяти. В любом случае нет проблем с неизменяемыми объектами нет.
Барьеры памяти нужны в основном только для упорядочивания инструкций и не использования регистров. В большинстве это на совести компилятора
и солнце б утром не вставало, когда бы не было меня
Re[36]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 07.03.22 20:57
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Сказано, же что блокировка не на всю таблицу, а только на hash % размер таблицы.


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

S> Там проблема не в барьере памяти. В любом случае нет проблем с неизменяемыми объектами нет.


Есть. Re[34]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 07.03.22
Ад пуст, все бесы здесь.
Re[37]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 07.03.22 21:24
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Сказано, же что блокировка не на всю таблицу, а только на hash % размер таблицы.


C>В любом случае, на каждую операцию она не вызывается.

Не на каждую, а на вставку. Ну посмотри исходники. Для чтения там барьер памяти. Но ты то утверждаешь, что проблемы в объектах и ConcurrentDictionary должны обеспечивать их модификацию.
S>> Там проблема не в барьере памяти. В любом случае нет проблем с неизменяемыми объектами нет.

C>Есть. Re[34]: ConcurrentDictionary vs reference type
Автор: Codealot
Дата: 07.03.22


Это пример того как выстреливать себе в ногу. Как я уже приводил пример тебе.
Здесь идет захват переменной в замыкании. Там вообще отдельный класс. Это вообще другая опера.
И поле в твоем примере не readonly. Private, но изменяемое внутри объекта. Какой же оно неизменяемое?
Если ты можешь менять поле внутри объекта, то позаботься о volatile различными способами
и солнце б утром не вставало, когда бы не было меня
Re[38]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 07.03.22 21:34
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Но ты то утверждаешь, что проблемы в объектах и ConcurrentDictionary должны обеспечивать их модификацию.


ЩИТО?

S> Это пример того как выстреливать себе в ногу. Как я уже приводил пример тебе.

S>Здесь идет захват переменной в замыкании. Там вообще отдельный класс. Это вообще другая опера.

Ты бредишь. Где ты там нашел замыкание?
Ад пуст, все бесы здесь.
Re[39]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.03.22 07:55
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>>Но ты то утверждаешь, что проблемы в объектах и ConcurrentDictionary должны обеспечивать их модификацию.


C>ЩИТО?


S>> Это пример того как выстреливать себе в ногу. Как я уже приводил пример тебе.

S>>Здесь идет захват переменной в замыкании. Там вообще отдельный класс. Это вообще другая опера.

C>Ты бредишь. Где ты там нашел замыкание?

Вот здесь по ссылке https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2#compiler-optimizations
Что касается твоей ссылки https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2#example-lazy-initialization

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

Там проблема не в барьере памяти. В любом случае нет проблем с неизменяемыми объектами нет.


Но ты утверждаешь, что есть и даешь ссылку http://rsdn.org/forum/message/8224254.1
Автор: Codealot
Дата: 07.03.22

Но никаких readonly полей там нет!
и солнце б утром не вставало, когда бы не было меня
Re[40]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 16:11
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Вот здесь по ссылке


Это всего лишь один пример. Речь идет про операции с памятью вообще.

As mentioned in the first article, the compiler might optimize the code in a way that reorders memory operations.


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


readonly важно только в том случае, если ты собираешься менять объекты которые уже созданы и используются другими тредами. Memory reordering будет и если он есть, и если его нет.

S>Но никаких readonly полей там нет!


Ты вообще не понимаешь, о чем идет речь. Услышал звон, да не понял где он.
Ад пуст, все бесы здесь.
Re[25]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 16:23
Оценка:
Здравствуйте, ·, Вы писали:

·>Твоё заявление: "CIL generators должны нам обеспечить 42", неверно, это ошибка. Ничего в стандарте 42 не обеспечивает, только текущие детали имплементации платформы от microsoft это обещают.


So, the CLR JIT makes an extra effort (beyond what’s mandated in the ECMA C# spec) to keep the most common variant of incorrect lazy initialization working on ARM.

Охохо.
Ад пуст, все бесы здесь.
Re[41]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.03.22 16:29
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Вот здесь по ссылке


C>Это всего лишь один пример. Речь идет про операции с памятью вообще.

C>

C>As mentioned in the first article, the compiler might optimize the code in a way that reorders memory operations.


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


C>readonly важно только в том случае, если ты собираешься менять объекты которые уже созданы и используются другими тредами. Memory reordering будет и если он есть, и если его нет.

readonly важно тем, что инициализируешь только в конструкторе. И они потокобезопасны! Их нельзя менять!
А в однопоточном конструкторе глубоко наплевать на Memory reordering!

Я тебе привел примеры классов которые можно безопасно использовать в ConcurrentDictionary

S>>Но никаких readonly полей там нет!


C>Ты вообще не понимаешь, о чем идет речь. Услышал звон, да не понял где он.


Приведи пример. Тебя вообще мало кто понимает.

Ответь на вопрос

Там проблема не в барьере памяти. В любом случае нет проблем с неизменяемыми объектами нет.



Но ты утверждаешь, что есть и даешь ссылку http://rsdn.org/forum/message/8224254.1
Автор: Codealot
Дата: 07.03.22
и солнце б утром не вставало, когда бы не было меня
Re[42]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 16:39
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>readonly важно тем, что инициализируешь только в конструкторе. И они потокобезопасны! Их нельзя менять!

S>А в однопоточном конструкторе глубоко наплевать на Memory reordering!

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

S> Приведи пример. Тебя вообще мало кто понимает.


Топик такой, что его вообще крайне мало кто понимает. Хотя тех, кто думает что понимает — очень много.
Ад пуст, все бесы здесь.
Re[44]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 08.03.22 16:58
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Ну и меняют они в конструкторе. Объясни в чем проблема?



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

S> Угу тебе уже кучу доводов привели. И про voliatil написали. Но у тебя какие то тараканы в голове и мешанина.


Не у меня.
Вот тебе конкретный пример:
// Warning: Bad code
class LazyExample
{
  private BoxedInt _boxedInt; // Note: This field is not volatile
  int GetInt()
  {
    BoxedInt b = _boxedInt; // Read 1
    if (b == null)
    {
      b = new BoxedInt(42); // Write 1 (inside constructor)
      _boxedInt = b;        // Write 2
    }
    return b._value;        // Read 2
  }
}


Обрати внимание — поле в BoxedInt присваивается только в конструкторе.
Ад пуст, все бесы здесь.
Re[45]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.03.22 17:07
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Ну и меняют они в конструкторе. Объясни в чем проблема?


C>

C>Меняют вообще в любой момент, когда им захочется.
C>До тебя не доходит, что такие перестановки могу привести к тому, что с точки зрения другого треда ссылка на объект будет доступна раньше записей, который его инициализируют?

S>> Угу тебе уже кучу доводов привели. И про voliatil написали. Но у тебя какие то тараканы в голове и мешанина.


C>Не у меня.

C>Вот тебе конкретный пример:
C>
C>// Warning: Bad code
C>class LazyExample
C>{
C>  private BoxedInt _boxedInt; // Note: This field is not volatile
C>  int GetInt()
C>  {
C>    BoxedInt b = _boxedInt; // Read 1
C>    if (b == null)
C>    {
C>      b = new BoxedInt(42); // Write 1 (inside constructor)
C>      _boxedInt = b;        // Write 2 // Это разве конструктор!!!
C>    }
C>    return b._value;        // Read 2
C>  }
C>}
C>


C>Обрати внимание — поле в BoxedInt присваивается только в конструкторе.


Оно не readonly! int GetInt() не конструктор! GetInt объекта может вызываться из разных потоков!
и солнце б утром не вставало, когда бы не было меня
Re[47]: ConcurrentDictionary vs reference type
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 08.03.22 17:43
Оценка:
Здравствуйте, Codealot, Вы писали:

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


S>> Оно не readonly! int GetInt() не конструктор!


C>Проблема — в new BoxedInt(42) и ссылке на объект. Напряги мозги.


Нет проблема в
_boxedInt = b;


Ты устанавливаешь _boxedInt не в конструкторе.
BoxedInt b = _boxedInt; // Read 1
    if (b == null)
    {
      b = new BoxedInt(42); // Write 1 (inside constructor)
      _boxedInt = b;        // Write 2 // Это разве конструктор!!!
    }

S>>GetInt объекта может вызываться из разных потоков!

C>Не может быть!

Конечно! Ибо твой пример то не в кассу! Собери наконец мозги!
Ты все крутишься вокруг volatile причем в отличие от процессоhного memory barier https://habr.com/ru/post/130318/

Производительность Thread.Volatile* и ключевого слово volatile

На большинстве платформ (точнее говоря, на всех платформах, поддерживаемых Windows, кроме умирающей IA64) все записи и чтения являются volatile write и volatile read соответственно. Таким образом, во время выполнения ключевое слово volatile не оказывает никакого влияния на производительность. Напротив, методы Thread.Volatile*, во-первых, несут накладные расходы на сам вызов метода, помеченный как MethodImplOptions.NoInlining, и, во-вторых, в текущей реализации, создают полный барьер памяти. То есть, с точки зрения производительности, в большинстве случаев предпочтительнее использование ключевого слова.


Но на тебе лежит потокобезопасность объекта и производительность. И никакой ConcurrentDictionary тебе не поможет.
и солнце б утром не вставало, когда бы не было меня
Re[45]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 09.03.22 09:48
Оценка:
Здравствуйте, Codealot, Вы писали:

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


C>Не у меня.

C>Вот тебе конкретный пример:
C>
C>// Warning: Bad code
C>class LazyExample
C>{
C>  private BoxedInt _boxedInt; // Note: This field is not volatile
C>  int GetInt()
C>  {
C>    BoxedInt b = _boxedInt; // Read 1
C>    if (b == null)
C>    {
C>      b = new BoxedInt(42); // Write 1 (inside constructor)
C>      _boxedInt = b;        // Write 2
C>    }
C>    return b._value;        // Read 2
C>  }
C>}
C>


C>Обрати внимание — поле в BoxedInt присваивается только в конструкторе.


ну и что с этим кодом не так?
Может ли быть создано больше одного BoxedInt? Да, может.
Может-ли GetInt() вернуть не 42? Нет, не может.

Вернемся к начальному вопросу из 1-го сообщения:
C> ConcurrentDictionary гарантирует безопасность объектов (если исходить из предположения, что они read-only)?

Какой опасности подвергаются или могут подвергаться readonly объекты хранящиеся в ConcurrentDictionary?
Re[46]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 09.03.22 15:22
Оценка:
Здравствуйте, xpalex, Вы писали:

X>ну и что с этим кодом не так?


Ты читай, а не упирайся рогом.

X>Может ли быть создано больше одного BoxedInt? Да, может.


Проблема не в этом.
Ад пуст, все бесы здесь.
Re[47]: ConcurrentDictionary vs reference type
От: xpalex  
Дата: 09.03.22 16:09
Оценка:
Здравствуйте, Codealot, Вы писали:

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


X>>ну и что с этим кодом не так?


C>Ты читай, а не упирайся рогом.


Т.е ты приводишь какой-то код и просишь найти где в нем что-то может пойти не так с точки зрения твоего незнания memory model CLR 2.0

C> b = new BoxedInt(42);


тут две записи. сначала будет записано 42 в выделенную область памяти (выделено под BoxedInt), потом адрес выделенного объекта будет записан в b.
mm clr 2.0 гарантирует. что эти операции не будут переупорядочены. (для x86, x64 это бесплатно, для ia64 и arm64 будут добавлены мемори барьеры.)

никакое переупорядочение чтений не сможет привести к попытке чтения поля объекта до чтения ссылки на объект. т.к. переупорядочиваться могут только операции с разными участками памяти.
или в твоем представлении возможно прочитать значение по адресу [p] до того как прочитан адрес p?

X>>Может ли быть создано больше одного BoxedInt? Да, может.


C>Проблема не в этом.


Так ты ж не говоришь в чем проблема. Привел код, но свое ожидание от этого кода не указал. Или мы по
C> // Warning: Bad code
должны понять что ты хочешь сделать не то, что написал?

Какую задачу своим кодом реашал и не решил?
Re[48]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 09.03.22 16:55
Оценка:
Здравствуйте, xpalex, Вы писали:

X>Т.е ты приводишь какой-то код и просишь найти где в нем что-то может пойти не так с точки зрения твоего незнания memory model CLR 2.0


Это из той же ссылки, которую я давал тебе лично и которую ты очевидно не читал. https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/january/csharp-the-csharp-memory-model-in-theory-and-practice-part-2
Кстати, с чего ты решил что свет сошелся клином именно на версии 2.0? Это единственная, которую ты знаешь?

X>или в твоем представлении возможно прочитать значение по адресу [p] до того как прочитан адрес p?


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

X>Какую задачу своим кодом реашал и не решил?


Свои задачи я решаю всегда, в отличие от трепачей, которых я встречал в своей жизни куда больше, чем мне хотелось бы.
Ад пуст, все бесы здесь.
Re[50]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 14.03.22 17:58
Оценка:
Здравствуйте, xpalex, Вы писали:

X>Ты ниже кода, котрый ты скопипастил из статьи не прочитал что-ли?


1) Прочитал. А ты в курсе, что текущая версия — уже давно 6, и в ней в отличии от 4.5 поддерживается к примеру ARM, в котором гарантии которые дает железо совсем другие?
2) "Код нарушает спецификацию, но все равно работает, так что пофиг" — за такое надо вообще гнать из профессии, улицы мести. Или как минимум наказывать каторжными работами в техсаппорте.

X>А в CLR 4 (2010 год) в отношении гарантий по памяти ничего не поменялось. CLR 3 не было, CLR 5+ еще не было. Или ты не знал?


См выше.

X>Для .net, который компилируется в CIL и только потом CLR-ом в нейтив — нет. Т.к. CLR добавляет барьеры для платформ там где требуется.


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

X>Тогда уточни вопрос из первого сообщения темы: что за опасность подстерегает объекты, когда они хранятся в ConcurrentDictionary?

X>А прям идеально было бы кусок кода, который ведет себя некорректно при использовании ConcurrentDictionary.

Повторю еще раз, для самых-самых чукчеписателей.
Если там есть гарантированные барьеры памяти (в том числе и при извлечении), то никаких проблем быть не должно.
Если же никаких гарантий нет, как утверждает samius
Автор: samius
Дата: 19.02.22
, то использовать CD таким образом — некорректно.
Доходит?
Ад пуст, все бесы здесь.
Re[52]: ConcurrentDictionary vs reference type
От: Codealot Земля  
Дата: 15.03.22 18:11
Оценка:
Здравствуйте, xpalex, Вы писали:

X>Пример подкладывания соломки? Похоже я пропустил. Я вообще не видел от тебя примеров кода, кроме попыток попказать какой бывает reordering.


Ты вообще пропускаешь всё, что не укладывается в твою картину мира.

This is the relevant part of LazyExample (recall that none of the fields are volatile):
XML

b = new BoxedInt();
b._value = 42; // Write 1
// DMB will be emitted here
_boxedInt = b; // Write 2

Because the CLR JIT emits the DMB instructions prior to the publication of the object into the _boxedInt field, Write 1 and Write 2 will not be reordered. And because ARM respects data dependence, the reads in the lazy initialization pattern will not be reordered either, and the code will work correctly on ARM.

So, the CLR JIT makes an extra effort (beyond what’s mandated in the ECMA C# spec) to keep the most common variant of incorrect lazy initialization working on ARM.


Также повторю про пример из сообщения выше, который совершенно точно некорректен согласно стандарту — как английским по белому написано в статье, но ты оправдывался тем что "но в версии 4.5 же всё равно работает, значит никаких проблем нигде быть не должно!!!111"
Ад пуст, все бесы здесь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.