Ленивый <T>ExecutionAndPublication - примеры, которые могут вызвать тупик - PullRequest
13 голосов
/ 29 мая 2011

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

Итак, подведем итог, мои вопросы:

  1. Можно ли вызвать тупик, инициализирующий ChannelFactory с помощью ExecutionAndPublication?

  2. Каковы возможные способы вызвать тупик, инициализирующий другие объекты с помощью ExecutionAndPublication?

Предположим, у вас есть следующий код:

class x
{
   static Lazy<ChannelFactory<ISomeChannel>> lcf = 
        new Lazy<ChannelFactory<ISomeChannel>>(
        () => new ChannelFactory<ISomeChannel>("someEndPointConfig"), 
        LazyThreadSafetyMode.ExecutionAndPublication
        );

    public static ISomeChannel Create()
    {
        return lcf.Value.CreateChannel();
    }
}

1 Ответ

10 голосов
/ 29 мая 2011
  1. Это так же задокументировано - если оно не использует никаких блокировок, это использование не может вызвать никаких тупиков.
  2. Представьте, что у вас есть ленивое значение, которое вы инициализируете, читая из базы данных, но вы хотите убедиться, что только один поток обращается к БД в любой момент. Если у вас есть другой код, который обращается к БД, вы можете зайти в тупик. Рассмотрим следующий код:
void Main()
{
    Task otherThread = Task.Factory.StartNew(() => UpdateDb(43));
    Thread.Sleep(100);
    Console.WriteLine(lazyInt.Value);
}

static object l = new object();
Lazy<int> lazyInt = new Lazy<int>(Init, LazyThreadSafetyMode.ExecutionAndPublication);

static int Init()
{
    lock(l)
    {
        return ReadFromDb();
    }
}

void UpdateDb(int newValue)
{
    lock(l)
    {
        // to make sure deadlock occurs every time
        Thread.Sleep(1000);

        if (newValue != lazyInt.Value)
        {
            // some code that requires the lock
        }
    }
}

Init() читает из БД, поэтому он должен использовать блокировку. UpdateDb() пишет в БД, поэтому ему также нужна блокировка, и поскольку Lazy в этом случае тоже использует блокировку внутри, это вызывает тупик.

В этом случае было бы легко устранить тупик, переместив доступ к lazyInt.Value в UpdateDb() вне оператора блокировки, но в других случаях это может быть не так тривиально (или очевидно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...