Re[3]: Double-checking
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.06.13 08:35
Оценка: 16 (1) +1
Здравствуйте, <Аноним>, Вы писали:

А>а как с Lazy такое делается?


private static readonly Lazy<Foo> _instance = new Lazy<Foo>(CreateFoo);
public static Foo ImmutableInstance { get { return _instance.Value; } }
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[5]: Double-checking
От: TK Лес кывт.рф
Дата: 14.06.13 09:14
Оценка: 1 (1) -1
Здравствуйте, Sinix, Вы писали:

S>Остался последний вопрос: при чём здесь

S>

S> the above code may not function as expected due to the memory model not guaranteeing the order of reads and writes

S>? Что-то они не то курили

Посмотрите, для каких целей используется volatile. due to the memory model not guaranteeing the order of reads and writes возможна ситуация, что для instance = new Foo(); в instance ссылка уже записалась, а результаты работы new Foo() еще нет. т.е. второй поток будет видеть "неинициализированный" экземпляр.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[2]: Double-checking
От: Sinix  
Дата: 13.06.13 12:05
Оценка: +1
Здравствуйте, bl-blx, Вы писали:

BB>Соответственно, гарантируется, что любые операции записи до записи в volatile будут видимы

BB>остальными потоками после записи в volatile.

Ну... да, но какое отношение оно имеет имеет к коду в примере? Там ошибка — не в гипотетическом переупорядочивании операций, ошибка в кривой реализации double check-паттерна:
    if (instance == null) // (1)
    {
      lock (padlock)
      {
        if (instance == null)
        {
          instance = new Foo();
          // (2)
          instance.Init();
        }
      }
    }


Сценарий: поток 1 отработал до точки (2) и был вытеснен, поток 2 проснулся и начал с точки (1). Приятной отладки
Re[3]: Double-checking
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 13.06.13 12:29
Оценка: +1
Здравствуйте, Sinix, Вы писали:

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


BB>>Соответственно, гарантируется, что любые операции записи до записи в volatile будут видимы

BB>>остальными потоками после записи в volatile.

S>Ну... да, но какое отношение оно имеет имеет к коду в примере? Там ошибка — не в гипотетическом переупорядочивании операций, ошибка в кривой реализации double check-паттерна:

S>
S>    if (instance == null) // (1)
S>    {
S>      lock (padlock)
S>      {
S>        if (instance == null)
S>        {
S>          instance = new Foo();
S>          // (2)
S>          instance.Init();
S>        }
S>      }
S>    }
S>


S>Сценарий: поток 1 отработал до точки (2) и был вытеснен, поток 2 проснулся и начал с точки (1). Приятной отладки


Именно так! Поток 2 увидит ссылку на Foo() до того, как для неё выполнится Init().
Собственно решение и предлагает заменить внутренний if так, чтобы публиковать только
полностью сконструированный instance. Поток 2 будет видеть null до тех пор,
пока поток 1 не опубликует полностью сконструированный объект.
El pueblo unido jamás será vencido.
Re[7]: Double-checking
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.06.13 10:26
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Во-вторых (если речь про win store/win phone app) — часть багов может не всплыть в эмуляторе.


Лично мне проще отлаживаться на дивайсе все равно.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[9]: Double-checking
От: TK Лес кывт.рф
Дата: 15.06.13 08:19
Оценка: +1
Здравствуйте, Sinix, Вы писали:

S>Мы по ходу про разные вещи говорим


Очень на то похоже
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Double-checking
От: Аноним  
Дата: 13.06.13 09:42
Оценка:
Привет
поясните

здесьjetbrains пишут


The second is to perform the initialization in a non-checked variable and then assigned it to the checked one, thus eliminating the ordering problem. Thus, the code in the inner if statement from the above example could be re-written as:

var temp = new Foo();
temp.Init();
instance = temp;


а почему в данному случае очередность операций гарантируется? потому что очередность не гарантируется только над одним объектом?
Re: Double-checking
От: Sinix  
Дата: 13.06.13 11:06
Оценка:
Здравствуйте, Аноним, Вы писали:

А>а почему в данному случае очередность операций гарантируется? потому что очередность не гарантируется только над одним объектом?

Если я ничего не проглядел — там написан бред. "memory model not guaranteeing the order of reads and writes" относится к обращениям к переменной из разных потоков, в приведённом коде используется lock да ещё и с даблчеком.

Если добавить код, обращающийся к instance напрямую — тогда ещё будет смысл, возможно.
Вместо этого танца с граблями я бы использовал
public static readonly Foo ImmutableInstance = CreateFoo();


Если создание Foo — дорогая операция, обернул бы в Lazy.
Re: Double-checking
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 13.06.13 11:39
Оценка:
Здравствуйте, Аноним, Вы писали:

А>
А>The second is to perform the initialization in a non-checked variable and then assigned it to the checked one, thus eliminating the ordering problem. Thus, the code in the inner if statement from the above example could be re-written as:

А>var temp = new Foo();
А>temp.Init();
А>instance = temp;
А>


А>а почему в данному случае очередность операций гарантируется? потому что очередность не гарантируется только над одним объектом?


Очерёдность операций гарантируется семантикой volatile поля в модели памяти.
Операция записи в volatile не может быть переупорядочена с предыдущими операциями записи.
Об этом можно прочитать, к примеру здесь (см. "Publication via Volatile Field")
Соответственно, гарантируется, что любые операции записи до записи в volatile будут видимы
остальными потоками после записи в volatile.
El pueblo unido jamás será vencido.
Re[4]: Double-checking
От: Sinix  
Дата: 13.06.13 12:45
Оценка:
Здравствуйте, bl-blx, Вы писали:

Остался последний вопрос: при чём здесь

the above code may not function as expected due to the memory model not guaranteeing the order of reads and writes

? Что-то они не то курили
Re[3]: Double-checking
От: Аноним  
Дата: 13.06.13 13:19
Оценка:
Здравствуйте, Sinix, Вы писали:

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


BB>>Соответственно, гарантируется, что любые операции записи до записи в volatile будут видимы

BB>>остальными потоками после записи в volatile.

S>Ну... да, но какое отношение оно имеет имеет к коду в примере? Там ошибка — не в гипотетическом переупорядочивании операций, ошибка в кривой реализации double check-паттерна:

S>
S>    if (instance == null) // (1)
S>    {
S>      lock (padlock)
S>      {
S>        if (instance == null)
S>        {
S>          instance = new Foo();
S>          // (2)
S>          instance.Init();
S>        }
S>      }
S>    }
S>


S>Сценарий: поток 1 отработал до точки (2) и был вытеснен, поток 2 проснулся и начал с точки (1). Приятной отладки


А что будет? instance не будет проинициализирован и при этом использован?
Re[4]: Double-checking
От: Sinix  
Дата: 13.06.13 13:29
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А что будет? instance не будет проинициализирован и при этом использован?

Угу, поток 2 получит доступ к instance до того как поток 1 вызовет instance.Init();
Re[2]: Double-checking
От: Аноним  
Дата: 14.06.13 05:56
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Аноним, Вы писали:


А>>а почему в данному случае очередность операций гарантируется? потому что очередность не гарантируется только над одним объектом?

S>Если я ничего не проглядел — там написан бред. "memory model not guaranteeing the order of reads and writes" относится к обращениям к переменной из разных потоков, в приведённом коде используется lock да ещё и с даблчеком.

S>Если добавить код, обращающийся к instance напрямую — тогда ещё будет смысл, возможно.

S>Вместо этого танца с граблями я бы использовал
S>
S>public static readonly Foo ImmutableInstance = CreateFoo();
S>


S>Если создание Foo — дорогая операция, обернул бы в Lazy.


а как с Lazy такое делается?
Re[3]: Double-checking
От: Sinix  
Дата: 14.06.13 08:32
Оценка:
Здравствуйте, Аноним, Вы писали:

А>а как с Lazy такое делается?


Примерно так (.net 4 и выше)
public static readonly Lazy<Foo> ImmutableInstance = new Lazy<Foo>(() => CreateFoo());
Re[6]: Double-checking
От: Sinix  
Дата: 14.06.13 09:52
Оценка:
Здравствуйте, TK, Вы писали:

TK>Посмотрите, для каких целей используется volatile. due to the memory model not guaranteeing the order of reads and writes возможна ситуация, что для instance = new Foo(); в instance ссылка уже записалась, а результаты работы new Foo() еще нет. т.е. второй поток будет видеть "неинициализированный" экземпляр.


Поведение в приведённом коде не зависит от того, помечена переменная instance как volatile или нет — баг будет в любом случае. Что самое поганое — отловить такую вещь в рантайме будет очень сложно.

Во-первых, Monitor.Exit работает как memory barrier.

Во-вторых (если речь про win store/win phone app) — часть багов может не всплыть в эмуляторе. Под x86/x64 ничего не поменялось, all writes are volatile и само ключевое слово по-прежнему служит лишь для предупреждения опасных оптимизаций со стороны jit-а/компилятора (но это не значит, что его не надо использовать).

А вот с ARM есть нюансы:

We’ve done targeted work to strengthen the memory model on ARM—specifically, we’ve inserted memory barriers at key points when writing to the managed heap to guarantee type safety—but we’ve made sure to only do this with a minimal impact on performance. The team went through multiple design reviews with experts to make sure that the techniques applied in the ARM CLR were correct. Moreover, performance benchmarks show that .NET code execution performance scales the same as native C++ code when compared across x86, x64 and ARM.


Называется, понимайте как хотите, никаких гарантий. Другими словами — используем volatile (а ещё лучше — lock) и не выпендриваемся
Re[4]: Double-checking
От: Аноним  
Дата: 14.06.13 17:13
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, <Аноним>, Вы писали:


А>>а как с Lazy такое делается?


AVK>
AVK>private static readonly Lazy<Foo> _instance = new Lazy<Foo>(CreateFoo);
AVK>public static Foo ImmutableInstance { get { return _instance.Value; } }
AVK>


правилно я понимаю, что если _instance нужен нестатичный, то просто везде убираем static?
Re[5]: Double-checking
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 14.06.13 18:56
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>правилно я понимаю, что если _instance нужен нестатичный, то просто везде убираем static?


Разумеется.
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
AVK Blog
Re[7]: Double-checking
От: TK Лес кывт.рф
Дата: 15.06.13 07:12
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Поведение в приведённом коде не зависит от того, помечена переменная instance как volatile или нет — баг будет в любом случае. Что самое поганое — отловить такую вещь в рантайме будет очень сложно.


S>Во-первых, Monitor.Exit работает как memory barrier.


В случае с _instance = new Foo() memory barrier в Monitor.Exit никак не поможет. Барьер нужен после new Foo() но, до присваивания результата в _instance. lock { _instance = new Foo(); } этого не предоставляет.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[8]: Double-checking
От: Sinix  
Дата: 15.06.13 07:42
Оценка:
Здравствуйте, TK, Вы писали:

TK>В случае с _instance = new Foo() memory barrier в Monitor.Exit никак не поможет. Барьер нужен после new Foo() но, до присваивания результата в _instance. lock { _instance = new Foo(); } этого не предоставляет.


Мы по ходу про разные вещи говорим

1. Monitor.Exit тут никак не может помочь, он только усложняет отлов ошибки, т.к. ещё сильнее сокращает окно времени для воспроизведения ошибки (угу, именно это я и написал выше, перечитайте )
2. Вот сам пример (предположим, instance.Init() состоит из одного присваивания):
   if (instance == null)
    {
      lock (padlock)
      {
        if (instance == null)
        {
          instance = new Foo();
          instance.SomeField = someVal; // <---
        }
      }
    }


От того, объявлена instance как volatile или нет, в этом примере ничего не зависит. Мы в любом случае можем получить побочные эффекты — сначала получим ссылку на instance, и только затем поток 2 присвоит значение в instance.SomeField.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.