关于锁的自动释放

这种方案一直在用,但是一直没能领会到它的意图,也是在一个面试过程中,被问到一个此类问题,从而才想到的。

    问题:当在一个函数中使用锁时,如果加锁后由于某种原因(疏忽,异常等),没有释放锁就直接退出了,那么将导致锁的状态错误,怎样解决这个问题?


1、没有正常释放锁:

就像下面的程序一样(这里仅作为示例,选用mutex):

#include <iostream>
#include <pthread.h>

using namespace std ;

pthread_mutex_t m ;

void print1(int i)
{
        pthread_mutex_lock(&m) ;
        if(i == 0)
        {
                cout<<"i == 0"<<endl ;
                // 这里没有释放锁
        }
        else
        {
                cout<<"i != 0"<<endl ;
                pthread_mutex_unlock(&m) ;
        }
}

int main()
{
        pthread_mutex_init(&m, NULL) ;

        // 试验1
        print1(1) ;
        print1(0) ;
        print1(2) ;

        pthread_mutex_destroy(&m) ;

        return 0 ;
}

运行过程如下:

[root@localhost mutex]# ./a.out
enter print1
print1: mutex_lock
i != 0
print1: mutex_unlock
exit print1

enter print1
print1: mutex_lock
i == 0
exit print1               # 这里没有unlock的打印

enter print1
print1: mutex_lock        # 由于受print1(0)时执行unlock,从而在lock时阻塞
# 阻塞在此

由结果可知:print1(1)运行正常,print1(0)时没有unlock,导致print1(2)中执行pthread_mutex_lock时挂起。

2、解决方案

相信很多人都用过同步或互斥机制,也都见过类似于下面的对锁机制的封装(这里选用mutex,当然具体的封装可能不同):

#include <iostream>
#include <pthread.h>

using namespace std ;

class MyMutex
{
public:
        MyMutex(pthread_mutex_t* mut)
        {
                _mutex = mut ;
        }
        ~MyMutex()
        {
                unlock() ;
        }

        void lock()
        {
                pthread_mutex_lock(_mutex) ;
        }
        void unlock()
        {
                if(_mutex)
                {
                        pthread_mutex_unlock(_mutex) ;
                        _mutex = NULL ;
                }
        }
private:
        pthread_mutex_t* _mutex ;
};

#define MUTEX_LOCK(m)           \
        MyMutex myMutex(&m);    \
        myMutex.lock() ;

#define MUTEX_UNLOCK()          \
        myMutex.unlock() ;

pthread_mutex_t m ;

void print2(int i)
{
        cout<<"enter print2"<<endl ;
        MUTEX_LOCK(m) ;
        if(i == 0)
        {
                cout<<"i == 0"<<endl ;
                // 这里没有释放锁
        }
        else
        {
                cout<<"i != 0"<<endl ;
                MUTEX_UNLOCK() ;
        }
        cout<<"exit print2"<<endl ;
}

int main()
{
        pthread_mutex_init(&m, NULL) ;

        // 实验2
        cout<<"print2(1)"<<endl ;
        print2(1) ;
        cout<<"print2(1) end"<<endl ;

        cout<<"print2(0)"<<endl ;
        print2(0) ;
        cout<<"print2(0) end"<<endl ;

        cout<<"print2(2)"<<endl ;
        print2(2) ;
        cout<<"print2(2) end"<<endl ;

        pthread_mutex_destroy(&m) ;

        return 0 ;
}

 

执行结果如下:

print2(1)
enter print2
mutex_lock
i != 0
mutex_unlock
exit print2
print2(1) end

print2(0)
enter print2
mutex_lock
i == 0
exit print2
mutex_unlock    # 顺序跟print2(1)的不同,在print2函数退出时执行的unlock
print2(0) end

print2(2)       # print2(2)打印信息与print1(1)打印信息顺序相同,
enter print2    # 所以没有受到print(0)中没有显式写unlock的影响
mutex_lock
i != 0
mutex_unlock
exit print2
print2(2) end

结论:在类的析构函数中释放锁,这样当退出函数时,超出类对象作用域,自动调用其析构函数,从而释放锁。

3、那么抛出异常的情况能否处理呢?

#include <iostream>
#include <pthread.h>

using namespace std ;

class MyMutex
{
public:
        MyMutex(pthread_mutex_t* mut)
        {
                _mutex = mut ;
        }
        ~MyMutex()
        {
                unlock() ;
        }

        void lock()
        {
                cout<<"mutex_lock"<<endl ;
                pthread_mutex_lock(_mutex) ;
        }
        void unlock()
        {
                if(_mutex)
                {
                        cout<<"mutex_unlock"<<endl ;
                        pthread_mutex_unlock(_mutex) ;
                        _mutex = NULL ;
                }
        }
private:
        pthread_mutex_t* _mutex ;
};

#define MUTEX_LOCK(m)           \
        MyMutex myMutex(&m);    \
        myMutex.lock() ;

#define MUTEX_UNLOCK()          \
        myMutex.unlock() ;

pthread_mutex_t m ;

void print2(int i)
{
        cout<<"enter print2"<<endl ;
        MUTEX_LOCK(m) ;
        if(i == 0)
        {
                cout<<"i == 0"<<endl ;
                // 这里跑出异常,也没有释放锁
                throw "" ;
        }
        else
        {
                cout<<"i != 0"<<endl ;
                MUTEX_UNLOCK() ;
        }
        cout<<"exit print2"<<endl ;
}

int main()
{
        pthread_mutex_init(&m, NULL) ;

        // 实验3
        cout<<"print2(1)"<<endl ;
        print2(1) ;
        cout<<"print2(1) end"<<endl ;
       try                                      # 需要有try,否则默认情况如果存在异常会终止程序执行
        {
                cout<<"print2(0)"<<endl ;
                print2(0) ;                     # 抛出异常
                cout<<"print2(0) end"<<endl ;
        }
        catch(...)
        {
                cout<<"catch one exception"<<endl ;
        }
        
        cout<<"print2(2)"<<endl ;
        print2(2) ;
        cout<<"print2(2) end"<<endl ;

        pthread_mutex_destroy(&m) ;

        return 0 ;
}

运行结果如下:

 

print2(1)
enter print2
mutex_lock
i != 0
mutex_unlock
exit print2
print2(1) end

print2(0)
enter print2
mutex_lock
i == 0                  # 抛出异常
mutex_unlock            # 同样执行了unlock
catch one exception     # 没有执行 cout<<"print2(0) end"<<endl ;

print2(2)               # print2(2)打印没有收到影响
enter print2
mutex_lock
i != 0
mutex_unlock
exit print2
print2(2) end

结论:异常情况下,时通过程序处理异常机制和类析构函数实现的。当函数抛出异常时,会根据函数调用栈逐步退栈,直到遇到try catch,如果没有try catch,则终止程序执行,在退栈过程中,类对象超出作用域时自动调用析构函数,从而释放锁。

对于其他的同步或互斥机制,可以采用同样的方法来处理,其原理是一样的。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值