1、动态顺序栈
固定大小的栈的容量可能随着参数数目的增加或者递归层数的增加而溢出,故此可用动态栈代替,实现思路较为简单,每次元素入栈前判断一下栈容量是否足够,不够的话增加容量,通过栈底指针移动。以下是参考代码:
#ifndef DY_STACK_H_INCLUDED
#define DY_STACK_H_INCLUDED
#include <malloc.h>
#include <stdlib.h>
#define STACK_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define ERROR -1
typedef int Status;
typedef int ElemType;
typedef struct Dysqstack
{
ElemType *top;
ElemType *bottom;
int stacksize;
}DySqStack;
//init
Status DySqStack_Init(DySqStack &S)
{
S.bottom=(ElemType *)malloc(STACK_SIZE*sizeof(ElemType));
if(!S.bottom) return ERROR;
S.top=S.bottom;
S.stacksize=STACK_SIZE;
return OK;
}
//push data to stack
Status DySqStack_Push(DySqStack &S,ElemType e)
{
//if size of stack is not enough,add the size by function named realloc
if(S.top-S.bottom>=S.stacksize-1)
{
S.bottom=(ElemType *)realloc(S.bottom,(STACKINCREMENT+STACK_SIZE)*sizeof(ElemType));
if(!S.bottom) return ERROR;
S.top=S.bottom+S.stacksize;
S.stacksize=S.stacksize+STACKINCREMENT;
}
S.top++;
*(S.top)=e;
return OK;
}
//pop data from stack
Status DySqStack_Pop(DySqStack &S,ElemType *e)
{
if(S.bottom==S.top)
{
std::cout<<"stack is empty!"<<std::endl;
return ERROR;
}
*e=*(S.top);
S.top--;
return OK;
}
Status DySqStack_notEmpty(DySqStack &S)
{
if(S.bottom==S.top)
{
std::cout<<"stack is empty!"<<std::endl;
return ERROR;
}
return OK;
}
Status DySqStack_Top(DySqStack &S,ElemType *e)
{
if(S.bottom==S.top)
{
std::cout<<"stack is empty!"<<std::endl;
return ERROR;
}
*e=*(S.top);
return OK;
}
#endif // DY_STACK_H_INCLUDED
2、线程安全的堆栈类
标准库中的stack结构是非线程安全的,为什么这么说呢?考虑一下stack的pop方法和top方法,pop方法只是出栈操作,top方法是取栈顶元素,设想一下有两个线程A和B,对某一个公共stack进行操作, 某一时刻A获得了stack的锁,A需要获取它的栈顶元素,而B则弹出它,需要top和pop两个方法,这就有个问题,我们这样写代码: if(!s,empty()) x=s.top();s.pop();当A调用了empty和top后,B可能调用pop弹出了最后一个元素使其成为空栈,而对空栈top是未定义的行为。这就是接口固有的条件竞争问题。
这里有个方法可以避免:
一、传入一个引用;
二、返回指向弹出值的指针;
#ifndef THREADSAFESTACK_H_INCLUDED
#define THREADSAFESTACK_H_INCLUDED
#include <memory>
#include <exception>
#include <mutex>
#include <stack>
struct empty_stack : std::exception
{
const char* what() const throw() {
return "empty stack!";
};
};
template<typename T>
class threadsafe_stack
{
private:
std::stack<T> data;
mutable std::mutex m;
public:
threadsafe_stack() : data(std::stack<int>()) {};
threadsafe_stack(const threadsafe_stack& other)
{
std::lock_guard<std::mutex> lock(other.m);
data=other.data;
}
threadsafe_stack& operator=(const threadsafe_stack&)=delete;
void push(T new_value)
{
std::lock_guard<std::mutex> lock(m);
data.push(new_value);
}
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=data.top();
data.pop();
}
bool empty()
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
};
#endif // THREADSAFESTACK_H_INCLUDED
下边是测试代码:
#include <iostream>
#include <thread>
#include <cmath>
#include <cstdlib>
#include <windows.h>
#include "threadsafestack.h"
using namespace std;
threadsafe_stack<int> s;
void fun1()
{
int value;
for(int i=1;i<=10;i++)
{
s.push(i);
}
while(!s.empty())
{
s.pop(value);
cout<<value<<endl;
}
}
void fun2()
{
int value;
for(int i=11;i<=20;i++)
{
s.push(i);
}
while(!s.empty())
{
s.pop(value);
cout<<value<<endl;
}
}
void read()
{
int value;
while(1)
{
Sleep(1000);
if(!s.empty())
{
s.pop(value);
cout<<"read: "<<endl;
cout<<value<<endl;
}
}
}
void write()
{
while(1){
Sleep(1000);
srand((unsigned)time(NULL));
int t=rand()%100;
s.push(t);
cout<<"write: "<<endl;
cout<<t<<endl;
}
}
int main()
{
thread t1(read);
thread t2(write);
t1.join();
t2.join();
return 0;
}