C # версия синхронизированного ключевого слова Java?

313

Есть ли у c # собственная версия ключевого слова "synchronized" в Java?

То есть в Java это может быть указано либо для функции, объекта или блока кода, например так:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}

или

public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}
Soraz
источник
3
Форма блока требует ссылки для блокировки. В форме метода объект блокировки неявно this (или Class [this.class, а не getClass ()] для статических методов, но не блокирует классы).
Том Хотин - tackline
1
Все еще не защищены? Я всегда прихожу сюда, потому что не могу вспомнить эту [MethodImpl(MethodImplOptions.Synchronized)]строчку.
Bitterblue
1
Я думаю, что ваш второй фрагмент не будет компилироваться - его нужно синхронизировать на чем-то.
PoweredByRice

Ответы:

466

Во-первых, большинство классов никогда не должно быть поточно-ориентированным. Используйте YAGNI : применяйте потокобезопасность только тогда, когда вы знаете, что на самом деле собираетесь его использовать (и тестировать).

Для вещей уровня метода есть [MethodImpl]:

[MethodImpl(MethodImplOptions.Synchronized)]
public void SomeMethod() {/* code */}

Это также может быть использовано на методах доступа (свойства и события):

private int i;
public int SomeProperty
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return i; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { i = value; }
}

Обратите внимание , что поля , как события будут синхронизированы по умолчанию, в то время как автоматический Реализуемое свойство не является :

public int SomeProperty {get;set;} // not synchronized
public event EventHandler SomeEvent; // synchronized

Лично мне не нравится реализация MethodImplблокировок thisили typeof(Foo)- что противоречит наилучшей практике. Предпочтительным вариантом является использование ваших собственных замков:

private readonly object syncLock = new object();
public void SomeMethod() {
    lock(syncLock) { /* code */ }
}

Обратите внимание, что для событий, подобных полям, реализация блокировки зависит от компилятора; в более старых компиляторах Microsoft это lock(this)/ lock(Type)- однако, в более поздних компиляторах он используетInterlocked обновления - так что потокобезопасен без неприятных частей.

Это позволяет более детально использовать и использовать Monitor.Wait/ Monitor.Pulseetc для связи между потоками.

Связанная запись в блоге (позже повторно ).

Марк Гравелл
источник
@earcam а у тебя вопрос? Это утверждение верно. Подавляющее большинство классов не требуют поточнобезопасности, не будут проверяться на поточность, а наличие поточности будет влиять на производительность. Число типов, которые действительно должны беспокоиться о потоках, очень мало - преднамеренно синхронизированные коллекции, мультиплексоры и т. Д.
Марк Грэвелл
4
Я думаю, что я должен был просто заявить; «большинству классов никогда не понадобится потокобезопасность», но «все разработчики должны учитывать параллелизм». Оглядываясь назад, я согласен, что это число очень мало (и определенно то, что вы хотите получить сразу, в одном месте, что позволяет большинству классов взаимодействовать, не обращая внимания на их многопоточное окружение). Жаль, что я удалил комментарий быстрее =)
earcam
6
В связанном сообщении Марка, опубликованном в марте 2010 года, говорится, что в .NET 4.0 MethodImplи полевые события теперь генерируют хороший код синхронизации, и вам больше не нужно использовать собственные блокировки.
Рори О'Кейн
2
Подавляющее большинство приложений в настоящее время основано на веб-технологиях и обслуживается средами, основанными на повторном использовании тяжелых экземпляров и сложном жизненном цикле объектов посредством внедрения зависимостей. Стандартное мышление в наши дни имеет тенденцию ошибаться в сторону безопасности потоков.
Sheepy
1
@Elazar слишком поздно, но для справки: изменение фреймворка - это однострочный переход к csproj, если вы создали его с помощью стандартных шаблонов .net / .net core - и обычный .net доступен, как и несколько -targeting. Тем не менее, инструменты IDE вокруг этого просто ужасны - вам просто нужно знать, что вы можете изменить и на что :)
Марк Гравелл
56
static object Lock = new object();

lock (Lock) 
{
// do stuff
}
Ян Грессманн
источник
10
Вы уверены, что хотите объявить объект блокировки как статический?
serg10
21
Конечно, каждый поток может легко получить к нему доступ без передачи ссылок.
Ян Грессманн
33
Если мы находимся в контексте вопроса задающего, то речь идет о методах экземпляра. Использование статического означает, что если поток 1 вызывает instance1.DoSomething (), а поток 2 вызывает instance2.DoSomething, второй вызов блокируется, даже если это совершенно другой объект. Вызов thread2 не должен блокироваться, если кто-то не вызывает DoSomething для того же объекта . Не сказать, что вы не правы, но сказать, что важно понимать эффект от использования статического здесь, потому что это может привести к плохой производительности, блокируя глобально, а не для каждого отдельного случая.
AaronLS
1
@AaronLS Статическая блокировка, если она очень полезна, когда ваш объект выполняет действия в большем объеме, чем он сам. Например, всегда с веб-сервисами.
Тибо Д.
4
-1, поскольку это поведение отличается от того, которое запрашивает OP. Это блокировка класса, а не блокировка экземпляра.
Tster
39

Есть ли у c # собственная версия ключевого слова "synchronized" в Java?

Нет. В C # вы явно указываете lockресурсы, с которыми хотите работать синхронно в асинхронных потоках. lockоткрывает блок; это не работает на уровне метода.

Однако основной механизм аналогичен, поскольку lockработает, вызывая Monitor.Enter(и впоследствии Monitor.Exit) во время выполнения. Java работает точно так же, в соответствии с документацией Sun .

Конрад Рудольф
источник
3
У него нет эквивалентного «ключевого слова», но, как показано выше в ответе Марка Гравелла, вы можете синхронизироваться на уровне метода с помощью аннотации [MethodImpl (MethodImplOptions.Synchronized)].
MindJuice
1
Так как synchronizedметод Java в принципе synchronized (this.getClass())не похож на C # lock(typeof(this))?
Шри Харша
2
@SriHarshaChilakapati, это только частично правильно, synchronizedключевое слово java для метода больше похоже на: synchronized(this)только для статического метода оно ведет себя как synchronized(class).
bvdb
6

Обратите внимание, с полными путями строка: [MethodImpl(MethodImplOptions.Synchronized)]должна выглядеть так

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

Traubenfuchs
источник
1
или вы можете просто использоватьusing System.Runtime.CompilerServices;
aloisdg переходит на codidact.com
Я написал этот комментарий, когда еще не знал об автоматической вставке операторов using, после того, как запрограммировал C # не более, чем на несколько дней или недель, и я поражен этими 3 ответами.
Traubenfuchs
1
Вы помогли как минимум 3 разработчикам, и это хорошо :)
aloisdg переходит на codidact.com
5

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

Джеймс
источник