在多线程中对相同的对象执行相同的操作是常见的(为了效率)。但是会有隐藏的隐患例如:
stack<int> a; if (a.empty()) //检查元素为空 { int c = a.pust(); //将栈顶元素推出 a.pop();//删除栈顶元素 //例如这里:如果a最后一个元素已经给推出,但是来自另一个线程的pop它检测到a里面还有元素于是进续下面的操作,就会出现接口固有的竞争 }
一般出现接口竞争是组织(类)中的互斥元保护粒度不够,但是保护粒度过高会导致多线程的优势全无,甚至会更差。
关于stack出栈的问题:主要问题是在于如果pop返回出栈指但是有内存约束或资源不够的情况下会赋值会出现bad_alloc异常。或者出栈值将赋值给其他对象但是在返回调用者的时候出现了异常,这时候可能会失去出栈值。
避免方法:
传入引用:但是这个会使程序陷入一个被动的情况,因为你传入的对象必须和stack一样的类型,这对某种的类型这是行不通的,因为构造一个实例在时间和资源方面是非常昂贵的。而且还有一个限制条件是必须是可赋值的(有些类型没有move函数和复制构造函数)。
要求不引发异常的移动函数和构造函数:你可以使用is_nothrow_move_constructiible 和is_nothrow_copy_constructible类检查是否有会引发异常的move 和copy函数但是这会受到限制。而且现在的C++标准会引发的异常的移动函数越来越少了,因右值的引用的支持。
返回指向出栈项的指针:通过返回指向出栈项的指针,而不是通过指针。优点是不会引发copy异常。但是管理不便,这种管理成本可能超过直接返回值的成本。但是也不是没有办法可以使用shared_ptr的指针类型。它不但避免了内存的泄漏,一旦最后一个指针销毁了,它也会销毁,这就避免了new和delete的使用。
同时提供选项1-2-3的方案:使你的程序增加灵活性。为用户提供选择的能力。
一个线程安全堆栈的示范定义:通过删减接口,使其数据容易给控制。