2014-11-06 1 views
3

В общеизвестных Joseph Albahari's article on Threading все переменные класса, используемые несколькими потоками, объявляются как static fields => все потоки имеют доступ к одной и той же переменной. Это должно быть оборудовано механизмом lock() во всех местах чтения/записи, и работа выполнена.Обмен данными во время многопоточности - это нестатические переменные ОК?

Мой вопрос о реализации свойств класса. Я понимаю, что это справедливо, если я реализую (например) имущества Timeout используя static подкладочный магазин:

class MyClassWithWorkerThread { 
    private readonly object _locker = new object(); 
    private static int _Timeout = false; 
    private int Timeout { 
     get { 
      lock (_locker) { 
       return _Timeout; 
      } 
     } 
     set { 
      lock (_locker) { 
       _Timeout = value; 
      } 
     } 
    } 
} 

Это делает переменную _Timeout общего числа всех экземпляров класса.

Но в моем случае многопоточная обработка является частной для экземпляра класса. Он начинается с New() и заканчивается Dispose(). Как основной поток, так и рабочий доступ к потоку Timeout (но _Timeout бэкэйд-магазин никогда не открывается за пределами объекта getter/setter).

Я не хочу _Timeout ценность быть прикладной. Я хочу, чтобы он был уникальным для каждого экземпляра класса. Мой вопрос: могу ли я безопасно удалить static от _Timeout переменной, чтобы достичь этого?

Примечание: Извините, если в коде есть ошибки, я фактически использую VB.NET, и я преобразовал его с помощью инструмента. Надеюсь, главный вопрос остается ясным.

ответ

3

Абсолютно безопасен и очень рекомендуется (статические переменные являются болезненными для тестирования даже когда это требуется). Предполагая, что с безопасной вы имели в виду действует то, что вы не должны забывать о том, что:

this.Timeout = 0; // This is safe and valid 
++this.Timeout; // This is safe but not valid 

Поскольку ++ оператор не является атомарным (именно поэтому мы имеем Interlocked класса). Конечно же применить в этой ситуации:

if (this.Timeout == 0) 
    Timeout = 10; 

Потому что даже если каждый доступ безопасного (я бы сказал, чтение свойства всегда безопасно, но вы можете прочитать старое значение без lock барьера), это не является атомарной операция и значение может измениться после теста и перед новым назначением. Еще сложнее?

if (this.Timeout == 0) 
    Timeout = Timeout * 2; 

В этом случае каждый раз, когда вы читаете Timeout вы можете получить другое значение. По этой причине я сказал, что блокировка внутри свойства редко бывает полезной, если только это свойство только для чтения.Гораздо лучше, чтобы удалить эту блокировку от свойств получить/установить и обернуть свой код в замок заявление:

lock (_locker) { 
    if (this.Timeout == 0) 
     Timeout = Timeout * 2; 
} 

также отметить, что для int _Timeout (я предполагаю, что назначение с false просто опечатка), вы можете просто удалить блокировку и сделать это volatile:

private volatile int _Timeout; 

конечно, это не решит другие описанные проблемы, но это может быть полезно (и быстрее) для свойства только для чтения (или довольно контролируемых ситуаций, volatile модификаторов могут быть хитрыми и имеет другое значение по сравнению с C, и легко забыть t шляпа до доступ их атомный, но не более).

+0

Я полностью согласен с использованием блоков 'lock()' (VB: 'SyncLock ... End SyncLock'), которые вы описываете. На самом деле я намеревался избежать сценариев, которые вы выделили, создав локальную копию «Timeout» (с помощью операции однократного чтения), когда это необходимо, поскольку в методах класса она не записывается. Я не уточнил этот вопрос, но понимаю эту концепцию блокировки. Но, по крайней мере, другие читатели получат выгоду от вашего подробного ответа, поэтому спасибо. – miroxlav

+0

Летучие поля * очень атомные. Очевидно, что выполнение двух операций по порядку не является атомарным, но затем не блокируется дважды в последовательности. – Voo

+0

@Voo, что вы правы, мое предложение довольно неясно: _access_ для переменных volatile является атомарным, но операторы, которые _seems_ для выполнения атомной операции, не являются (например, ++). –

1

Статическое ключевое слово является ортогональным (то есть: независимым или не связанным с) потоком.

Статическое ключевое слово должно использоваться только в том случае, если требуется только один экземпляр этого свойства/field/class/method/etc.

В вашей ситуации вам не нужна статика на _Timeout, если вы хотите, чтобы это значение было уникальным для экземпляра.

Я не читал эту статью вы ссылку, но я думаю, что вы, возможно, не понял его (или автор неправильно использовать статические)

+0

Несмотря на то, что упомянутая статья превосходна и рекомендована многими и многими, я вижу, что 'static' немного злоупотребляют там, даже в примерах, где это необязательно требуется. Но теперь, было ясно, спасибо. – miroxlav

+0

@miroxlav да, я полагаю, что он использовал статические почти везде для _simplicity_ и чтобы сократить образцы кода короче (объекты экземпляра не создаются и не проходят). С быстрым взглядом, и я думаю, что это не то, что он предлагает для потоковой передачи, просто удобный синтаксис. –

Смежные вопросы