.NET线程知识点

2019/01/09 254

多线程

什么是多线程

线程(Thread)是操作系统能够进行运算调度的最小单位。大部分情况下,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并行多个线程,每条线程并执行不同的任务。

在多核或多 CPU,或支持 Hyper-threading 的 CPU 上使用多线程程序设计的好处是显而易见的,即提高了程序的执行吞吐率。在单 CPU 单核的计算机上,使用多线程技术,也可以把进程中负责 I/O 处理,人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的 workhorse 线程执行密集计算,从而提高了程序的执行效率。

多线程:多个线程并发技术。

从 .NET Framework 4 开始,任务并行库和 PLINQ 提供了多线程程序中的任务并行和数据并行 API

为多线程处理同步数据

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

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

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

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

死锁和争用条件

多线程处理解决了吞吐量和响应性问题,但引入此功能带来了新的问题:死锁和争用条件

死锁

两个线程中的每一个线程都尝试锁定另外一个线程已锁定的资源时,就会发生死锁。两个线程都不能继续执行。

托管线程处理类的许多方法都提供了超时设定,有助于检测死锁。例如,下面代码尝试在 lockObject 对象上获取锁。如果在 300 毫秒内没有获取🔒, 返回 False

if (Monitor.TryEnter(lockObject, 3000))
{
    try
    {
        // Place code protected by the Monitor here.
    }
    finally
    {
        // Monitor.Exit(lockObject);
    }
}
else
{
    // Code to execute if the attempt times out.
}

争用条件

争用条件是程序的结果取决于两个或多个线程中的哪一个先到达某一特定代码块时出现的一种 bug。多次运行程序会产生不同的结果,并且无法预测任何给定运行的结果。

争用条件的一个简单例子是递增一个字段。假定某个类有一个私有 static 字段,每创建该类的一个实例时它都递增一次,使用的代码是 objCt++。此操作要求将 objCt 的值加载到寄存器中,使该值递增,然后存储到 objCt 中。

在多线程应用程序中,一个已加载并递增该值的线程可能会被另一个线程抢先,抢先的线程执行全部三个步骤;第一个线程继续执行并存储其值时,它会覆盖 objCt,但不考虑该值在其三厅执行期间已更改这一事实。

通过使用 Interlocked 类的方法(如 Interlocked.Increment),可以轻松避免这种争用条件。若要了解多个线程间同步数据的其它技巧,请参阅为多线程处理同步数据

争用条件也可能会在多个线程的活动时发生。编写每一行代码时,都不许考虑出现以下情况时会发生什么情况:一个线程在执行该行代码前,其他线程抢先执行了改代码。

线程安全

线程安全:某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间地共享变量,使程序能正确完成。

线程安全的规则:

类库相关建议

为多线程处理设计类库时,请考虑以下准则:

评论