Java的synchronized关键字在C#中有对应的版本吗?

352

c#有没有自己的版本像Java中的"synchronized"关键字一样?

也就是说,在Java中,它可以被指定为函数、对象或者代码块,像这样:

public synchronized void doImportantStuff() {
   // dangerous code goes here.
}
或者
public void doImportantStuff() {
   // trivial stuff

   synchronized(someLock) {
      // dangerous code goes here.
   }
}

3
块形式需要一个锁的引用。在方法形式中,锁对象隐式地是this(或Class [this.class,而不是getClass()]对于静态方法,但不要锁定类)。 - Tom Hawtin - tackline
3
还没有保护吗?我总是来这里,因为我记不住那个[MethodImpl(MethodImplOptions.Synchronized)]的语句。 - Bitterblue
1
我认为你的第二个片段无法编译 - 它需要在某些东西上进行同步 - PoweredByRice
5个回答

520

首先,大多数类不需要线程安全。使用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的实现方式,因为它会锁住thistypeof(Foo) - 这违反了最佳实践。首选方法是使用自己的锁:

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

请注意,对于类似字段的事件,锁定实现取决于编译器;在旧的 Microsoft 编译器中,它是 lock(this)/lock(Type)但在更近期的编译器中,它使用 Interlocked 更新,因此是线程安全的,没有不好的部分。
这允许更精细的使用,并允许使用 Monitor.Wait/Monitor.Pulse 等在线程之间通信。
相关的博客文章(稍后重新审视)。

6
我认为我应该简单地表述:“大多数类不需要是线程安全的”,但“所有开发人员必须意识到并发性”。回想起来,我同意这个数字非常小(而且绝对是你希望在一个地方正确处理一次,从而允许大多数类无视它们的多线程环境相互作用)。真希望我能更快地删除评论=)。 - earcam
8
Marc的博客文章有一个2010年3月的跟进文章,指出在.NET 4.0中,MethodImpl和类似字段的事件现在生成良好的同步代码,不再需要使用自己的锁。 - Rory O'Kane
2
现在大多数应用程序都是基于Web的,使用依赖注入通过复杂对象生命周期和重复利用实例的框架提供服务。现在默认的思维倾向于保证线程安全。 - Sheepy
我收到了 'MethodImplOptions' 不包含 'Synchronized' 的定义。 - Elazar
1
@Elazar 现在为时已晚,但是记录一下:如果您使用 .net standard / .net core 模板创建了 csproj,则更改框架只需要一行代码即可完成 - 并且常规的 .net 可用,多目标也可用。然而,围绕此的 IDE 工具非常糟糕 - 您只需要知道可以更改什么以及如何更改 :) - Marc Gravell
显示剩余14条评论

68
static object Lock = new object();

lock (Lock) 
{
// do stuff
}

13
你确定要将你的锁对象声明为静态的吗? - serg10
22
没问题,因此每个线程都可以轻松地访问它,而不需要在各个线程之间传递引用。 - Jan Gressmann
40
如果我们站在提问者的问题背景下讨论,那么我们正在谈论实例方法。使用静态方法意味着,如果线程1调用instance1.DoSomething(),线程2调用instance2.DoSomething(),尽管这是两个完全不同的对象,第二个调用仍然会被阻塞。除非有人在同一个对象上调用DoSomething,否则线程2的调用不应该被阻塞。并不是说你错了,而是说重要的是要理解在此处使用静态方法的影响,因为它可能会导致性能不佳,通过全局阻塞而不是按实例进行阻塞。 - AaronLS
1
@AaronLS 静态锁在对象执行比自身更大范围的操作时非常有用。例如,Web服务经常使用静态锁。 - Thibault D.
7
-1表示这与OP所询问的行为不同。这是类锁,而不是实例锁。 - tster
使用静态锁:假设一些你从未考虑过的奇怪线程访问此“静态”“Lock”对象以进行其他锁定目的 - 意味着不在此代码块上。然后,该奇怪线程获取了锁。现在,您的线程池要访问您的代码块,但是由于奇怪线程尚未释放“Lock”对象,因此它们中的任何一个都无法进入。我认为这是非常特殊的情况,您需要知道其影响才能继续。如果使用实例方法,请勿将“static”锁用作常规实践。我来自Java,但是我想概念应该是相同的 :) - sura2k

39

C#有自己的版本的Java "synchronized"关键字吗?

没有。在C#中,您需要显式地lock要在异步线程之间同步工作的资源。 lock打开一个块;它不适用于方法级别。

然而,底层机制是类似的,因为lock通过在运行时调用Monitor.Enter(随后是Monitor.Exit)来工作。根据Sun文档,Java也是这样工作的。


3
它没有等效的“关键字”,但是如上面Marc Gravell的回答所示,您可以使用[MethodImpl(MethodImplOptions.Synchronized)]注释在方法级别上进行同步。 - MindJuice
1
由于Java中的synchronized方法基本上是synchronized(this.getClass()),那么在C#中类似的方法不是应该是lock(typeof(this))吗? - Sri Harsha Chilakapati
2
@SriHarshaChilakapati,那只是部分正确的,Java中synchronized关键字在方法上更像是:synchronized(this),只有在静态方法上才会像synchronized(class)一样。 - bvdb

6
请注意,使用完整路径时,行:[MethodImpl(MethodImplOptions.Synchronized)]应该看起来像这样:[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]

3
或者你只需使用using System.Runtime.CompilerServices; - aloisdg
我写下这个评论时还不知道自动插入using语句的功能,当时我只学习了几天或几周的C#编程,所以对于那三个赞感到非常惊讶。 - ASA
1
你帮助了至少三个开发者,这很不错 :) - aloisdg

5
您可以使用lock语句来替换。我认为这只能替换第二个版本。此外,请记住,synchronizedlock都需要在对象上操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接