当前位置:嗨网首页>书籍在线阅读

26-线程安全

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

3.14 线程安全

线程是指在一个进程中的执行单元。绝大多数进程只有一个线程。不过,一个进程也可以持有多个线程,每个线程执行自己的代码逻辑。我们称这种进程为“多线程(multithreaded)”。可以把多线程的进程理解成有多个进程共享同一地址空间,如果没有显式协调,线程会在任意时刻、以任意方式运行。在多处理器系统中,同一个进程的两个或多个线程可能会并发执行。在访问共享数据时,有两种方式可以避免修改它:一是采取数据同步访问(synchronized access)机制(通过加锁实现),二是把数据存储在线程的局部变量中(thread-local,也称为线程封闭thread confinement)。

支持线程的操作系统提供了锁机制(锁是指可以保证相互排斥的程序结构),从而保证线程之间不会互相干扰。标准I/O使用了这些机制,保证单个进程的多个线程可以同时发起标准I/O调用——甚至可以对于同一个流发起多个调用,不会由于并发操作而彼此干扰。但是,这些机制通常还不能满足需求。举个例子,在某些情况下,你希望给一组调用加锁,把一个I/O操作的临界区(critical region,是指一段独立运行的代码,不受其他线程干扰)扩大为几个I/O操作。而在另外一些情况下,你可能希望取消全部锁,以提高程序效率[1]。本节中,我们将会讨论如何处理这两种情况。

标准I/O函数在本质上是线程安全的。在每个函数的内部实现中,都关联了一把锁、一个锁计数器,以及持有该锁并打开一个流的线程。每个线程在执行任何I/O请求之前,必须首先获得锁而且持有该锁。两个或多个运行在同一个流上的线程不会交叉执行标准I/O操作,因此,在单个函数调用中,标准I/O操作是原子操作。

当然,在实际应用中,很多应用程序需要比独立函数调用级别更强的原子性。举个例子,假设一个进程中有多个线程发起写请求,由于标准I/O函数是线程安全的,各个写请求不会交叉执行导致输出混乱。也就是说,即使两个线程同时发起请求操作,加锁可以保证执行一个写请求后再执行另一个写请求。但是,如果进程连续发起很多写请求,希望不但不同线程的写请求不会交叉,同一线程的写请求也不会交叉,该怎么做呢?为了支持该功能,标准I/O提供了一系列函数,每个函数可以操纵和流关联的锁。