这种方案一直在用,但是一直没能领会到它的意图,也是在一个面试过程中,被问到一个此类问题,从而才想到的。
问题:当在一个函数中使用锁时,如果加锁后由于某种原因(疏忽,异常等),没有释放锁就直接退出了,那么将导致锁的状态错误,怎样解决这个问题?
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、没有正常释放锁:
就像下面的程序一样(这里仅作为示例,选用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,则终止程序执行,在退栈过程中,类对象超出作用域时自动调用析构函数,从而释放锁。
对于其他的同步或互斥机制,可以采用同样的方法来处理,其原理是一样的。