在native用c++ coding的时候,多线程并发是很常见的情况,这个时候需要用锁来管理公共资源;还有一种常见情况是,在debug的时候需要关注一个函数进来和出去的信息。
这两种情况会增加一些额外的代码,可能使你的代码看起来不是那么美好,而且在函数比较大且出口比较多的时候还容易忘记在某个出口进行操作,如果只是打印信息只是统计不出来,如果是忘记了释放锁,那这个线程就死锁了会造成更多稳定性问题。通过利用C++对象的生命周期可以让你的代码变的更简洁美好而且高效,android系统源代码里大量的使用了类似的设计。
1.多线程并发是很常见的情况,这个时候需要用锁来管理公共资源
假如一个函数大概是这样的,我们要通过锁来锁住里边的资源,每一个出口都要加上pthread_mutex_unclok,是不是看上去不爽,而且还容易忘。
int func1() {pthread_mutex_lock();if(con1) { Res.value=1; pthread_mutex_unlock(); return Res; }Res.op1();Res.op2();if(con2){ Res.op3(); pthread_mutex_unlock(); return Res;}...Res.op18()return Res;pthread_mutex_unlock();}
android源码里提供了一个Mutex完美的解决了这个问题,使用Mutex后函数变成了这样的。
是不是简洁了好多,还不用担心忘记在哪个出口释放。int func1() {Mutex::Autolock lock(mLock);if(con1) { Res.value=1; return Res; }Res.op1();Res.op2();if(con2){ Res.op3(); return Res;}...Res.op18()return Res;}
其实原理很简单,利用了lock这个对象的生命周期,和它的构造函数和析构函数。func1函数进来的时候创建了Mutex::Autolock类的临时变量对象lock,会执行构造函数在构造函数里lock住mLock,当func1函数返回退出的时候无论在哪个出口,函数调用栈结束的最后,临时变量对象lock的生命周期结束,并对对象进行回收执行析构函数,在析构函数里unlock mLock,完成整个锁和解锁的过程。
不过在早期的ndk里并没有提供这个api,后来的版本里有了,所以为了程序的兼容性,可以自己实现 Mutex和Autolock,就是拷贝android的源代码了,100行都不到。下边是我在normandie项目里拷贝的源代码,熟悉c++语法的话很容易理解。/** AutoMutex from AOSP*//#ifndef _NMD_UTILS_AUTOMUTEX_H/#define _NMD_UTILS_AUTOMUTEX_H/#include/#include /#include /#include /#include /#include /* Simple mutex class. The implementation is system-dependent.* The mutex must be unlocked by the thread that locked it. They are not* recursive, i.e. the same thread can't lock it multiple times.*/class NmdMutex {public: enum { PRIVATE = 0, SHARED = 1 }; NmdMutex(); NmdMutex(const char* name); NmdMutex(int type, const char* name = NULL); ~NmdMutex(); // lock or unlock the mutex status_t lock(); void unlock(); // lock if possible; returns 0 on success, error otherwise status_t tryLock(); // lock the mutex, but don't wait longer than timeoutMilliseconds. // Returns 0 on success, TIMED_OUT for failure due to timeout expiration. // // OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep // capabilities consistent across host OSes, this method is only available // when building Android binaries. status_t timedLock(nsecs_t timeoutMilliseconds); // Manages the mutex automatically. It'll be locked when Autolock is // constructed and released when Autolock goes out of scope. class Autolock { public: inline Autolock(NmdMutex& mutex) : mLock(mutex) { mLock.lock(); } inline Autolock(NmdMutex* mutex) : mLock(*mutex) { mLock.lock(); } inline ~Autolock() { mLock.unlock(); } private: NmdMutex& mLock; };private: // A mutex cannot be copied NmdMutex(const NmdMutex&); NmdMutex& operator = (const NmdMutex&); pthread_mutex_t mMutex;};// ---------------------------------------------------------------------------inline NmdMutex::NmdMutex() { pthread_mutex_init(&mMutex, NULL);}inline NmdMutex::NmdMutex(__attribute__((unused)) const char* name) { pthread_mutex_init(&mMutex, NULL);}inline NmdMutex::NmdMutex(int type, __attribute__((unused)) const char* name) { if (type == SHARED) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(&mMutex, &attr); pthread_mutexattr_destroy(&attr); } else { pthread_mutex_init(&mMutex, NULL); }}inline NmdMutex::~NmdMutex() { pthread_mutex_destroy(&mMutex);}inline status_t NmdMutex::lock() { return -pthread_mutex_lock(&mMutex);}inline void NmdMutex::unlock() { pthread_mutex_unlock(&mMutex);}inline status_t NmdMutex::tryLock() { return -pthread_mutex_trylock(&mMutex);}inline status_t NmdMutex::timedLock(nsecs_t timeoutNs) { const struct timespec ts = { /* .tv_sec = */ static_cast (timeoutNs / 1000000000), /* .tv_nsec = */ static_cast (timeoutNs % 1000000000), }; return -pthread_mutex_timedlock(&mMutex, &ts);}// ---------------------------------------------------------------------------/* * Automatic mutex. Declare one of these at the top of a function. * When the function returns, it will go out of scope, and release the* mutex.*/typedef NmdMutex::Autolock NmdAutoMutex;/#endif // _NMD_UTILS_AUTOMUTEX_H
2.还有一种常见情况是,在debug的时候需要关注一个函数进来和出去的信息
比如我想统计一下某个函数的耗时,或者想观察它是不是正常退出了。一般都会这么写。是不是看着有点上火。
void func2() {log(“in”);do1();if(con1) { do2(); log(“out”); return;}if(con2) { do3(); log(“out”); return;}if(con3) { do4(); log(“out”); return;}if(con4) { do5(); log(“out”); return;}log(“out”);}
还可以这么写
void func2() {TimeCacl cacl(“func2”);do1();if(con1) { do2(); return;}if(con2) { do3(); return;}if(con3) { do4(); return;}if(con4) { do5(); return;}}
只需要定义一个类
class TimeCacl {public: TimeCacl(char* func_name) { mFuncName.append(func_name); log(“%s in”, mFuncName.c_str()); } ~TimeCacl() { log(“%s out”, mFuncName.c_str()); }private: String8 mFuncName;}
跟Mutex::Autolock异曲同工,都是利用了对象的生命周期来达成目的的。