2009-09-04 2 views
2

Может ли кто-нибудь объяснить, почему примеры потоковой обработки всегда ставят объект (в частности, переменную-член) статичным, если к нему нужно обращаться несколькими потоками?C#: вопрос о безопасности потоков для переменных-членов

Моя проблема заключается в том, что создание переменной-члена-члена означает, что оно будет использоваться совместно со всеми другими экземплярами класса. Иногда я нахожу, что мне бы хотелось, чтобы несколько потоков внутри класса «касались» переменной-члена, но в то же время позволяли каждому объекту иметь свою собственную копию.

Будет ли ответ на выполнение того, что я упомянул просто замена:

  • Использование летучих ключевое слово
  • Использование блокировки (объект)

ответ

3

Если вы хотите, чтобы каждый поток использовал одну и ту же переменную-член, но также поддерживал отдельную копию, у вас есть противоречие. Либо они используют одну и ту же переменную, либо нет. Если я не пойму тебя правильно.

Сказав, что если вам действительно нужно статическое поле для доступа к нескольким потокам, каждый из которых сохраняет свое собственное, личное значение, вы можете использовать атрибут ThreadStatic. Этот атрибут гарантирует, что статическое поле будет приватным для каждого потока («поток локальный», как его обычно называют).

[ThreadStatic] 
private static bool s_threadHasDoneItsWork; 

Обратите внимание, что вы не можете инициализировать локальное статическое поле потоков через статический конструктор или непосредственно в качестве static type field = value. (Компилятор не будет жаловаться, но это не будет работать должным образом.)


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

Но это точно предел того, что он делает: только доступ к этому конкретному полю. Как только вы его прочитали, значение снова устарело, поэтому никогда не думайте о том, чтобы делать что-то вроде volatileField ++ (что означает «читать volatileField, добавлять его к только что прочитанному значению, устанавливать volatileField», а не «increment volatileField», вам нужно использовать класс Interlocked для этого, что намного дороже).

Безопасный способ использования изменчивых полей читает их напрямую, но когда вы их изменяете, используйте механизм блокировки, прежде чем вы их прочитаете или напишите. (Единственное разумное исключение, о котором я могу думать, это булевский флаг, например «Я сейчас сделан».)

Практическое использование летучих полей, не удивительно, весьма ограничено. Stick с простой блокировкой; ключевое слово lock и соответствующие методы класса Monitor заботятся о единственном пользователе и синхронизации памяти (как только вы входите и выходите из замка).

+0

Да, я видел атрибут Threadstatic на MSDN, и это меня сбило с толку. Кажется, это противоречит определению статической переменной, если каждый поток поддерживает свою личную копию. – Setheron

+0

Это делается в некотором смысле, да. Но это может быть полезно, если вам нужно поддерживать состояние, зависящее от потока, или указать, что методы, вызываемые из одного из ваших методов, должны иметь доступ, но не один. В зависимости от его использования, локальное хранилище потоков может предотвратить необходимость синхронизации потоков (так как нет обмена между потоками), поэтому это может быть полезно. – Ruben

+0

даже не имеет смысла, почему тогда потребуется статическое поле. Только Threadstatic должен иметь смысл. – Setheron

8

Там нет ничего, что требует от переменных-членов, чтобы быть статичными резьб.

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

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

+0

Должно ли ключевое слово volatile быть включено со статическим? Не существует проблем с кешированием статического значения в одном потоке и не получением последнего в другом потоке? – Setheron

+0

@Setheron: всегда используйте 'lock (object)' для доступа к переменным, общим для нескольких потоков. Это даст вам эксклюзивный доступ, и он сделает все для вас, чтобы все потоки всегда получали самые последние значения (барьер памяти). – dtb

+0

См. Мой ответ для использования волатильности. В большинстве практических случаев вы можете полностью игнорировать его. (Хотя летучий гуру, вероятно, может стать великолепным случаем для изменчивых, ИМХО, вам это не понадобится.) – Ruben