Здравствуйте, RAza, Вы писали:
S>>А чем, кстати, Interlocked не подошел?
RD>>3. И да, можно переписать на Interlocked.CompareExchange и обойтись без lock.
RA>Никогда не использовал это на практике. Вас не затруднит показать реализацию? И в двух словах объяснить в чем плюсы по сравнению с DCL?
Для начала, зачем все это надо.
1. Иногда код алгоритма на Interlocked-инструкциях получается чище, чем с применением примитивов синхронизации (lock, производные WaitHandle и т.п.)
2. Мы экономим объект (тот, на котором делается lock).
3. Interlocked-инструкции быстрее (ns против ms у других примитивов). Поэтому в высококонкурентных lock-free алгоритмах используют именно их.
И сразу оговорюсь, что рядовому разработчику корпоративного софта (ну типа меня

) исчезающе редко (т.е. практически никогда)
приходится писать код, где разница в производительности между Interlocked/не-Interlocked ощутимо влияет на performance.
Теперь по реализации. Покажу на примере getter'a для Value
public class Lazy<T> where T : class
{
private readonly Func<T> m_Factory;
private int m_Initializing = 0;
private T m_Value;
public Lazy(Func<T> factory) { m_Factory = factory; }
public T Value
{
get
{
var curVal = Volatile.Read(ref m_Value);
if (curVal != default(T)) return curVal;
// Самый простой и красивый вариант.
// Когда я предлагал Interlocked, то думал именно о нем.
// Атомарно проверяем, что m_Value == null, если да - инициализируем.
// К сожалениею, он не обеспечивает требование exact-once инициализации.
Interlocked.CompareExchange(ref m_Value, m_Factory(), default(T));
return Volatile.Read(ref m_Value);
// Вариант с exact-once инициализацией (без обработки ошибок)
if (Interlocked.CompareExchange(ref m_Initializing, 1, 0) == 0)
{
// Захватили право на инициализацию, инициируем
var val = m_Factory();
Volatile.Write(ref m_Value, val);
return val;
}
// Если мы попали сюда, то право на инициализацию захватил кто-то другой, будем ждать, пока он проинициализирует
while (true)
{
var val = Volatile.Read(ref m_Value);
if (val != default(T)) return val;
// Это так называемый Sleep-wait,
// В lock-free часто применяют SpinWait/Thread.Yield либо их комбинации
Thread.Sleep(TimeSpan.FromMilliseconds(5));
}
}
}
}
А если добавить обработку ошибок инициализации, то код будет еще сложнее. При этом профит от lock-free выглядит более чем сомнительным.
Так что рассматривай это скорее как академический пример.