Разница между блокировкой (это) и блокировкой статического объекта

Какой из следующих двух фрагментов кода лучше использовать?

static readonly object _locker = new object();
lock (_locker)

или

lock (this)

this - объект текущего экземпляра. Итак, почему lock (_locker) всегда в книгах?

Связанный:
В чем разница между блокировкой (это) и блокировкой (thisLock)?
Why is lock(this) {…} bad?

Ответ 1

Там может быть большая разница. Самое большое различие между ними состоит в том, что в первом примере используется один объект для блокировки (следовательно, ключевое слово static), а ключевое слово this во втором примере подразумевает блокировку экземпляра. Поэтому может быть большое отличие от перспективы производительности и даже с точки зрения правильности, но это зависит от кода внутри блокировки.

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

И есть еще одна проблема, но разница намного меньше, чем для ранее отмеченных различий. В первом примере используется закрытый объявленный объект для блокировки, в то время как другой использует указатель this, который является ссылкой на объект самого метода экземпляра. Поскольку эта ссылка общедоступна для других объектов, их можно заблокировать, что может привести к блокировкам в редких случаях. Если вы разработчик приложений, я бы не стал беспокоиться об этом (пока вы не блокируете такие вещи, как System.String или System.Type), но если вы разработчик фреймворка, вы, конечно же, не должны используйте lock(this), так как нет способа сказать, каким образом разработчики приложений (ab) будут использовать ваш код.

Ответ 2

Почти всегда предпочтительнее блокировать частный объект readonly.

Отличие состоит в том, что this обычно видимо для внешнего кода, что может занять его, т.е. -

var obj = new YourClass();
lock(obj)
{
    ...
}

... в этом случае любая попытка внутри YourClass до lock (this) блокируется.

Ответ 3

Поскольку вы не хотите, чтобы доступ к блокировке осуществлялся извне объекта.

Если вы используете блокировку (это), вы можете получить тупик:

void blah() {
   lock(this);
   sleep(200);
}

//Some other block of code

MyObject a;
foreach(Mythread m in threads)
{
   lock(a);
   m.Call(a.blah); //Not the best syntax, but you get the idea.
}

Если вы держите замок внутри объекта, он не будет блокироваться.