最简单的共享对象和多线程问题处理策略是通过确保 iostream 对象是线程局部对象来避免问题。例如,
不过在许多情况下(例如缺省共享标准流对象),使对象专用于某线程是不可能的,这就需要其他的策略了。
要独立对 iostream 类对象执行一序列操作,必须使用某种形式的锁定。锁定会增加一些开销,即使是在单线程应用程序中也是如此。是增加锁定还是使 iostream 对象成为线程的专用对象取决于为应用程序选择的线程模型:线程是独立的还是协同操作的?
如果每个独立的线程都使用其自己的 iostream 对象来生成或使用数据,则这些 iostream 对象专用于各自的线程,因此不需要锁定。
如果多个线程协同操作(即,共享同一个 iostream 对象),则必须同步对共享对象的访问,而且必须使用某种形式的锁定使序列化操作独立化。
iostream 库提供了 stream_locker 类用于锁定针对 iostream 对象的一系列操作。因此可以将动态启用或禁用 iostream 对象的锁定所造成的性能开销降到最低。
可以使用 stream_locker 类的对象使针对流对象的一序列操作独立化。例如,下例中所示代码尝试查找文件中的某一位置,并读取下一个数据块。
#include <fstream.h> #include <rlocks.h> void lock_example (fstream& fs) { const int len = 128; char buf[len]; int offset = 48; stream_locker s_lock(fs, stream_locker::lock_now); .....// open file fs.seekg(offset, ios::beg); fs.read(buf, len); } |
在此示例中,stream_locker 对象的构造函数定义了每次只能执行一个线程的互斥区域的开始位置。从函数返回后调用的析构函数定义了互斥区域的结束位置。stream_locker 对象确保了在文件中查找特定偏移和从文件中读取能够同时独立执行,并且在原来的 ThreadA 读取文件之前,ThreadB 不能更改文件偏移。
另一种使用 stream_locker 对象的方法是显式定义互斥区域。在以下示例中,为了使 I/O 操作和后续错误检查独立化,使用了 vbstream_locker 对象的成员函数 lock 和 unlock 调用。
{ ... stream_locker file_lck(openfile_stream, stream_locker::lock_defer); .... file_lck.lock(); // lock openfile_stream openfile_stream << "Value: " << int_value << "\n"; if(!openfile_stream) { file_error("Output of value failed\n"); return; } file_lck.unlock(); // unlock openfile_stream } |