Effective STL 第12条:切勿对STL容器的线程安全性有不切实际的依赖

在多线程中,对一个STL实现,我们最多只能期望:

1、多个线程读是安全的。多个线程可以同时读同一个容器的内容,并且保证是正确的。自然的,在读的过程中,不能对容器有任何写入操作。

2、多个线程对不同的容器做写入操作是安全的。多个线程可以同时对不同的容器做写入操作。


在一般情况下,我们可以有下面的解决方法:

1、自己手工做同步控制。

		vector<int> v;
		...
		getMutexFor(v);
		vector<int>::iterator first5(find(v.begin(), v.end(), 5));
		if (first5 != v.end())
		{
			*first5 = 0;
		}
		releaseMutexFor(v);


   2、相对上面的做法,更为面向对象的方法是创建一个Lock类,它在构造函数中获得一个互斥体,在析构函数中释放它,从而尽可能地减少getMutexFor调用没有相对应得releaseMutexFor调用的可能性。这样的类(实际上是一个类模板)看起来大概像这样:

		template<typename Container>                  	//一个为容器获取和释放互斥体的模板
		class Lock					//框架:其中的很多细节被省略了
		{
		public:
			Lock(const Container& container) :
			c(container)
			{
				getMutexFor(c);			//在构造函数中获取互斥体
			}
			~Lock()
			{
				releaseMutexFor(c);		//在析构函数中释放它
			}
		private:
			const Container & c;
		}

使用类(如Lock)来管理资源的生存期的思想通常被称为“获取资源时即初始化”(resource acquisition is initialization)。在任何一本C++书中都可以学习参考。但是,要记住,上面的例子只是给出了一个框架,实际开发中还需要一系列的增强。

		vector<int> v;
		...
		{					//创建新的代码块
			Lock<vector<int> > lock(v);      	//获取互斥体
			vector<int>::iterator first5(find(v.begin(), v.end(), 5));
			if (first5 != v.end()) {
				*first5 = 0;
			}
		}					//代码块结束,自动释放互斥体


因为Lock对象在其析构函数中释放容器的互斥体,所以很重要的一点是,当互斥体应该被释放时Lock就要被析构。为了做到这一点,我们创建了一个新的代码块(block),在其中定义了Lock,当不再需要互斥体时就结束该代码块。看起来好像是我们把“调用releaseMutexFor”这一任务换成了“结束代码块”,事实上这种说法是不确切的。如果我们忘了为Lock创建新的代码块,则互斥体仍然会被释放,只不过是晚一些——当控制到达包含block的代码块末尾时。而如果我们忘了调用releaseMutexFor,那么我们永远也不会释放互斥体。


而且,基于Lock的方案在有异常发生时也是强壮的。C++保证,如果有异常被抛出,局部对象会被析构,所以,即便在我们使用Lock对象的过程中有异常抛出,Lock仍会释放它所拥有的互斥体。如果我们依赖手工调用getMutexFor和releaseMutexFor,那么,当在调用getMutexFor之后而在调用releaseMutexFor之前异常抛出时, 我们将永远也无法释放互斥体。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值