Заблокировать свойства, хороший подход?

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

Когда клиент подключается, создается класс Client, где каждый клиент имеет свою переменную "A".

Иногда клиент вызывает такой метод:

Client selectedClient SelectOtherClientClassByID(sentID);

selectedClient.A=5;

Нет проблем до сих пор с этим, даже когда 5 классов выполняли одновременно (threadpool), но я думал о добавлении блокировок к свойствам A?

Как

A {
    get { return mA; }
    set {
        // use lock here for settting A to some value
    }    
}

Было бы хорошо?

Ответ 1

Вам нужно использовать блокировки в BOTH get и set. Этот замок должен быть одним и тем же объектом. Например:

private object mylock = new object();

public int A {

  get {
    int result;
    lock(mylock) {
    result = mA; 
    }
    return result;
  } 

  set { 
     lock(mylock) { 
        mA = value; 
     }
  }
}

Ответ 2

Блокирование доступа к свойствам внутри аксессуаров может привести к фиктивным результатам. В качестве примера рассмотрим следующий код:

class C {
    private object mylock = new object();

    public int A {

      get {
        int result;
        lock(mylock) {
        result = mA; 
        }
        return result;
      } 

      set { 
         lock(mylock) { 
            mA = value; 
         }
      }
    }
}
C obj = new C;
C.A++;

(да, я скопировал его с первого ответа) Здесь есть состояние гонки! Операция "C.A ++" фактически требует двух отдельных доступов к A, один для получения значения, а другой - для установки обновленного значения. Ничто не гарантирует, что эти два доступа будут выполняться вместе, без переключения контекста между ними. Классический сценарий состояния гонки!

Итак, остерегайтесь! Не стоит помещать блокировки внутри аксессуаров, блокировки должны быть явно получены, как и предыдущий ответ (хотя он не обязательно должен быть с SyncRoots, любой объект будет делать)

Ответ 3

Это очень редко, когда вам нужно всего лишь установить одно свойство. Чаще всего selectedClient.A = 5 будет частью гораздо большей логической операции, которая включает в себя несколько присвоений/оценок/и т.д. Во время этой операции вы предпочитаете, чтобы selectedClient находился в постоянном состоянии, а не вводил взаимоблокировки/условия гонки. Поэтому гораздо лучше выставить свойство SyncRoot в свой класс Client и заблокировать его от вызывающего кода:

Client selectedClient = GetClient(...);

lock(selectedClient.SyncRoot)
{
    selectedClient.A = 5;
    selectedClient.B = selectedClient.A * 54;
}