同步用于多线程的数据

当多个线程可以调用单个对象的属性和方法时,必须同步这些调用。 否则,一个线程可能会中断另一个线程正在执行的操作,并且该对象可能处于无效状态。 其成员不受此类中断保护的类称为线程安全。

.NET提供了多个策略来同步对实例和静态成员的访问:

  • 同步的代码区域。 可以使用 Monitor 类,或使用编译器对此类的支持,仅同步需要同步的代码块,从而提高性能。

  • 手动同步。 可以使用.NET类库提供的同步对象。 请参阅 同步基元概述,其中包括类 Monitor 的讨论。

  • 同步上下文。 仅对于 .NET Framework 应用程序,可以使用 SynchronizationAttribute 为 ContextBoundObject 对象启用简单的自动同步。

  • 命名空间中的 System.Collections.Concurrent 集合类。 这些类提供内置同步的添加和删除操作。 有关详细信息,请参阅 Thread-Safe 集合。

公共语言运行时提供了一个线程模型,其中类属于许多类别,这些类别可以根据要求以各种不同方式同步。 下表显示了为具有给定同步类别的字段和方法提供哪些同步支持。

Category 全局字段 静态字段 静态方法 实例字段 实例方法 特定代码块
无同步 否 否 否 否 否 否
同步上下文 否 否 否 是的 是的 否
同步的代码区域 否 否 仅当标记为 否 仅当标记时 仅当已标记时
手动同步 Manual Manual Manual Manual Manual Manual

无同步

这是对象的默认值。 任何线程随时都可以访问任何方法或字段。 一次只能有一个线程访问这些对象。

手动同步

.NET类库提供了许多用于同步线程的类。 请参阅 同步基元概述。

同步的代码区域

可以使用 Monitor 类或编译器关键字来同步代码块、实例方法和静态方法。 不支持同步的静态字段。

Visual Basic和 C# 都支持使用特定语言关键字、lockC# 中的语句或SyncLockVisual Basic中的语句标记代码块。 当代码由线程执行时,将尝试获取锁。 如果锁已被另一个线程获取,则线程会阻塞,直到锁变为可用为止。 当线程退出同步的代码块时,无论线程如何退出块,该锁都释放。

Note

在 .NET 9 和 C# 13 或更高版本中,如果lock目标是专用System.Threading.Lock实例,则lock语句用于Lock.EnterScope()创建同步区域。

Visual Basic 不支持在SyncLock中使用System.Threading.Lock,因此请继续将SyncLock与专用的私有引用类型配合使用。

如果 lock 目标不是 Lock 实例,并且对于所有 SyncLock 的用法,编译器使用 Monitor.Enter 和 Monitor.Exit。 由于该实现,你仍然可以在同步区域中使用其他 Monitor 方法。

还可以用值为 MethodImplOptions.Synchronized 的 MethodImplAttribute 修饰方法,其效果与使用 Monitor 或某个编译器关键字来锁定整个方法体相同。

Thread.Interrupt 可用于将线程从阻塞操作中中断,例如等待访问同步的代码区域。 Thread.Interrupt 还用于将线程从操作中分解出来,例如 Thread.Sleep。

Important

不要对类型加锁——也就是说,不要对 C# 中的 typeof(MyType)、Visual Basic 中的 GetType(MyType) 或 C++ 中的 MyType::typeid 加锁——以保护 static 方法(在 Visual Basic 中为 Shared 方法)。 请改用专用静态对象。 同样,不要在 C# 中使用 this(在 Visual Basic 中为 Me)来锁定实例方法。 请改用专用对象。 类或实例可能会被你自己的代码以外的代码锁定,从而导致死锁或性能问题。

编译器支持

Visual Basic和 C# 都支持使用Monitor.Enter和Monitor.Exit锁定对象的语言关键字。 Visual Basic支持 SyncLock 语句;C# 支持 lock 语句。

在这两种情况下,如果代码块中引发了异常,则由 lock 或 SyncLock 获取的锁会自动释放。 C# 和 Visual Basic 编译器会生成一个 try/finally 块,其中在 try 开始处调用 Monitor.Enter,并在 finally 块中调用 Monitor.Exit。 如果 lock 或 SyncLock 块内部引发了异常,则会运行 finally 处理程序,从而允许执行任何清除工作。

同步上下文

仅在 .NET Framework 应用程序中,可以在任何 ContextBoundObject 上使用 SynchronizationAttribute 来同步所有实例方法和字段。 同一上下文域中的所有对象共享相同的锁。 允许多个线程访问方法和字段,但每次只允许一个线程。

另见