为多线程处理同步数据

多个线程可以调用单个对象的属性和方法时,对这些调用进行同步处理是非常重要的。 否则,一个线程可能会中断另一个线程正在执行的任务,可能使该对象处于无效状态。 其成员不受这类中断影响的类叫做线程安全类。

.NET 提供了几种策略,用于同步对实例和静态成员的访问:

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

类别 全局字段 静态字段 静态方法 实例字段 实例方法 特定代码块
无同步 No No No No
同步上下文 No No
同步代码区域 仅当标记时 仅当标记时 仅当标记时
手动同步 手动 手动 手动 手动 手动 手动

无同步

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

手动同步

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

同步代码区域

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

Visual Basic 和 C# 都支持使用特定语言关键字标记代码块,在 C# 中使用的是 lock 语句,在 Visual Basic 中使用的是 SyncLock 语句。 由线程执行代码时,会尝试获取锁。 如果该锁已由其他线程获取,则在锁变为可用状态之前,该线程一直处于阻止状态。 线程退出同步代码块时,锁会被释放,与线程的退出方式无关。

注意

由于 lockSyncLock 语句是使用 Monitor.EnterMonitor.Exit 实现,因此 Monitor 的其他方法可以在同步区域内与它们结合使用。

还可以使用值为 MethodImplOptions.SynchronizedMethodImplAttribute 修饰方法,其效果和使用 Monitor 或其中一个编译器关键字锁定整个方法正文相同。

Thread.Interrupt 可用于中断对线程执行阻止操作(如等待访问同步代码区域)。 Thread.Interrupt 还用于中断对线程执行 Thread.Sleep 等操作。

重要

为保护 static 方法(Visual Basic 中的 Shared 方法),请不要锁定类型,即:C# 中的 typeof(MyType)、Visual Basic 中的 GetType(MyType) 或 C++ 中的 MyType::typeid。 请改用私有静态对象。 同样,不要使用 C# 中的 this(Visual Basic 中的 Me)锁定实例方法。 请改用私有对象。 类或实例可由不是你自己的代码锁定,这可能会引起死锁或性能问题。

编译器支持

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

在这两种情况下,如果代码块中引发异常,则 lockSyncLock 获取的锁将自动释放。 C# 和 Visual Basic 编译器在发出 try/finally 块时,在 try 的起始处使用 Monitor.Enter,在 finally 块中使用 Monitor.Exit。 如果 lockSyncLock 块内部引发了异常,则会运行 finally 处理程序,从而允许执行任何清除工作。

同步上下文

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

请参阅