首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

临界区堆叠出现的死锁

2013-10-29 
临界区重叠出现的死锁死锁例子:一些函数看其本身是线程安全的,但是当多个函数同时执行时出现了非线程安全。

临界区重叠出现的死锁
    死锁例子:

         一些函数看似其本身是线程安全的,但是当多个函数同时执行时出现了非线程安全。一个例子是多个函数在多线程下同时执行时临界区相互重叠

        Requset向Inventory注册,然后Invetory存有Request对象的指针。当Request对象析构自身时向Inventory中移除指针,此时若析构停住,然后Inventory持有该Request指针做一些操作,将访问到Request这个半成品对象。且为了保证每个函数线程安全采用锁保护,最后多个函数同时执行导致死锁。

#include<iostream>#include<string>#include<unistd.h>#include<pthread.h>#include<set>#include<boost/shared_ptr.hpp>#include<boost/weak_ptr.hpp>using namespace std;using namespace boost;class Request;class Inventory{    public:        void add(shared_ptr<Request> &a){            weak_ptr<Request> one(a);            pthread_mutex_lock(&mutex);            requests.insert(one);            pthread_mutex_unlock(&mutex);        }        void remove(shared_ptr<Request> &a){            weak_ptr<Request> one(a);            pthread_mutex_lock(&mutex);            requests.erase(one);            pthread_mutex_unlock(&mutex);        }        void printALL();        Inventory(){            pthread_mutex_init(&mutex,NULL);        }    private:        pthread_mutex_t mutex;        set<weak_ptr<Request> > requests;//持有所有Request对象的weak_ptr};Inventory g_inventory;class Request{    public:    //void process(){//###1###这里原本设计不需要参数a且和下面一句使用,这就出现了有两批shared_ptr对象在管理Request对象,最后在process退出时一批shared_ptr将Request对象析构,然后另一批对象即###2###处shared_ptr退出时再次析构shared_ptr造成double free...汗...还没有适应shared_ptr        void process(shared_ptr<Request>& a){//###3###            //shared_ptr<Request> a(this);//###1###            pthread_mutex_lock(&mutex);            g_inventory.add(a);            pthread_mutex_unlock(&mutex);        }        ~Request() __attribute__ ((noinline)){//告诉编译器此函数非内联            //pthread_mutex_lock(&mutex);            //sleep(1);            //g_inventory.remove(a);            //pthread_mutex_unlock(&mutex);            cout<<"~Request()"<<endl;        }        void print(){// __attribute__ ((noninline)){            pthread_mutex_lock(&mutex);            cout<<"Request print()"<<endl;            pthread_mutex_unlock(&mutex);        }    private:        pthread_mutex_t mutex;};void Inventory::printALL(){    shared_ptr<Request> one;    pthread_mutex_lock(&mutex);    for(set<weak_ptr<Request> >::iterator it=requests.begin();it!=requests.end();it++){        one=it->lock();//尝试提升weak_ptr,若提升成功则Request对象还存在,否则不存在。注意这个提升操作是线程安全的,这点至关重要        if(one){            one->print();            cout<<"lock success"<<endl;        }        else{            requests.erase(it);            //it--;//原本以为set会像vector的erase会返回一个迭代器那么需要先自减再自增这样才完全遍历容器,可是set不是那样,可见set底层应该是类似链表这样的数据结构实现的            cout<<"lock failure"<<endl;        }    }    cout<<"Inventory::printALL() unlocked"<<endl;    pthread_mutex_unlock(&mutex);}void* threadFun(void* arg){    shared_ptr<Request> one(new Request);//###2###采用shared_ptr管理Request对象生命周期    //one->process()//###1###    one->process(one);//###3###替换###1###处的解决方案    sleep(1);//###4###注释掉该句则:Requset对象析构了,Inventory::printALL()无打印对象    //delete req;}int main(){    pthread_t pid;    if(pthread_create(&pid,NULL,threadFun,NULL)<0)        cout<<"pthread_create error"<<endl;    usleep(500*1000);    g_inventory.printALL();    pthread_join(pid,NULL);    return 0;}

###4###不注释的输出:

Request print()
lock success
Inventory::printALL() unlocked
~Request()

###4###注释掉的输出:

~Request()
lock failure

注:这种方式存在轻微的内存泄露,当Request对象析构后,Inventory不会立即将相应的weak_ptr从容器中删除,而是到下一次遍历容器时才会删除(延迟删除)。


    死锁解决方式3:

                将print()移除printALL()的临界区,采用写时拷贝技术。一个简单的方法是:在Inventory::printALL()内用一个临时容器temp在临界区内复制requests_全部元素,然后temp在临界区外执行遍历操作。这样显然拷贝整个容器非上策。还有个方法是:采用shared_ptr管理set容器。明天再写...


       

热点排行