1. 程式人生 > >動態順序棧和執行緒安全的堆疊類

動態順序棧和執行緒安全的堆疊類

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;
}