1.1多线程基础
我认为在使用多线编程之前,了解下面的基本术语,对我们是有些帮助的。
进程是给定程序当前正在执行的实例;操作系统的一个基本功能就是管理进程。每个进程都包含一个或多个线程。进程由System.Diagnostics命名空间的Process类的实例表示。
单线程程序是仅包含一个线程的进程。多线程程序的进程则包含两个或更多的线程。
在多线程程序中运行时具有正确的表现,就说是线程安全的。代码的线程处理模型是指代码向调用者提出的一系列要求,只有保证这些要求才能保障线程安全。例如,许多类的线程处理模型都是“可以从任意线程调用静态方法,但实例方法仅能从分配实例的线程调用”。
任务是可能有高延迟的工作单元,目的是生成一个结果值,或产生想要的效果。任务和线程的区别是:任务代表需要执行的作业,而线程代表做这个作业的工作者。任务有Task类的实例表示。生成给定类型的值的任务由Task<T>类表示,后者从非泛型的Task类型派生。它们都在System.Threading.Tasks命名空间中。
线程池是多个线程的集合,也是决定如何向线程分配工作的逻辑。有任务要执行时,它分配线程池中的一个工作者线程执行任务,并在任务结束后解除分配。
多线程处理的目的和方式:
多线程处理主要用于两个方面:实现多任务和解决延迟。
如果有足够的内核使得每个线程都能分配到一个内核,那么每个线程就就都使用了它们各自的CPU。然而,现实是线程数远大于内核数。为了解决这个问题,OS通过时间分片(time slicing)的机制来模拟多个线程并发运行。OS以极快的速度从一个线程切换到另一个线程,给人的感觉就是所有的线程都在同时执行。处理器在切换到下一个线程执行,执行一个特定线程的时间周期称为时间片(time slice)/量子(quantum)。在一个给定的内核中改换执行线程的动作称为上下文切换。
无论是真正的多核并行运行,还是使用时间片技术来模拟,我们说“一起”进行的多个操作是并发的。为了实现这种并发的操作,需要以异步方式调用它,被调用操作的开始与结束都独立于调用它的控制流。异步分配的工作与当前控制流并行执行,就实现类并发性。并行编程是将一个问题分解成较小的部分,并异步地发起对每个部分的处理,使它们能并发执行。
多线程处理面临的问题:
相比单线程,多线程程序写起来复杂困难的多。根本原因在于单线程程序中的一些成立的假设在多线程程序中变得不成立了,主要包括下面四类。
a) 大多数操作都不是原子性的
b)竞态条件造成的不确定性
c)内存模型的复杂性
d)锁定造成的死锁