最近在多线程编程中遇到了这样一个情况,程序中有一些变量是全局有效的,多个线程都要访问,由于没有考虑太多,导致线程出现一些问题。
于是乎,就想到了互斥锁,可是遇到了更严重的情况:有些线程执行一段时间后会被其父线程杀掉,假若此时它已对互斥锁执行了加锁操作而又未解锁的话,就造成了死锁的情况,导致程序频频出错。
这的确是一个令人困惑的问题,如何在线程被杀掉后还能够将锁解开着实令我为难了。
还好,线程的加锁、解锁是通过一个函数来进行的,函数入口处加锁、出口处解锁。于是乎,灵光一闪,我何不声明一个类呢,该类实例在函数入口处声明对互斥锁进行加锁操作,出口处解锁。
哈,为了确保加锁、解锁一定是配对的,我们何不利用一下C++里面类的构造函数和析构函数呢。如果我不通过new的方式声明该类实例,而是直接将内存分配在栈上,在函数出口处无论如何它都会被析构的。
请看:
TLock类头文件:
#ifndef _TLOCK_
#define _TLOCK_
// 调试输出宏
#define _MY_TEST_
#undef _MY_TEST_
// 名称:自动加锁、解锁类TLock
// 功能:为多线程提供加、解锁功能
// 用法:不要采用new的方法来声明实例,否则自动加解锁功能将失效
#include <iostream.h>
#include <pthread.h>
#ifdef DEBUG
#include "LOG.hpp"
#endif
static pthread_mutex_t lockMutex=PTHREAD_MUTEX_INITIALIZER;
class TLock
{
private:
pthread_mutex_t *mylock;
public:
TLock(pthread_mutex_t *lock);
~TLock();
};
#ifdef DEBUG
extern LOG log;
extern unsigned long LOG_DEV;
#endif
#endif
TLock类实现文件:
#include "TLock.hpp"
// 在函数中直接声明TLock类实例,可以确保该函数结束时互斥锁自动解锁
// TLock构造函数
TLock::TLock(pthread_mutex_t *lock)
{
#ifdef _MY_TEST_
cout << "begin to lock mutex..." << endl;
#endif
#ifdef DEBUG
log.write(LOG_DEV|TRIVAL|LOG_TIME_TID, "线程加锁操作.../n");
#endif
mylock = lock;
pthread_mutex_lock(mylock); // 加锁操作
}
// TLock析构函数
TLock::~TLock()
{
#ifdef _MY_TEST_
cout << "begin to unlock mutex..." << endl;
#endif
#ifdef DEBUG
log.write(LOG_DEV|TRIVAL|LOG_TIME_TID, "线程解锁操作.../n");
#endif
pthread_mutex_unlock(mylock); // 解锁操作
}
各位看明白了吧,其实是很简单的。我在需要加解锁的函数中是这样加解锁的:
int myfunc(int index)
{
TLock lock(&lockMutex);
//...
}
关键在这里:TLock lock(&lockMutex);
构造函数中将mp(互斥锁指针)传进去自动加锁,然后就不管了,在函数myfunc退出时系统会自动的清除TLock的实例lock,因为它是被声明在栈上的。而作为一个类,被清除时析构函数一定会及时登场的,于是乎,我们可爱的互斥锁就不得不被解锁 ^_^
后话,修改后的加解锁操作表现的好像还不错,程序也稳定了不少。如果你也遇到了类似的问题,不妨试一试这个用法。