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

3834

被折叠的 条评论
为什么被折叠?



