本节书摘来自异步社区《深入解析Android 5.0系统》一书中的第6章,第6.2节Android native层的同步方法,作者 刘超,更多章节内容可以访问云栖社区“异步社区”公众号查看
6.2 Android native层的同步方法
深入解析Android 5.0系统
Android在Linux提供的线程同步函数的基础上进行了二次封装,让实现线程同步更加简单方便。这些同步类和函数在native层的代码中出现的非常频繁。
6.2.1 互斥体Mutex和自动锁Autolock
Mutex和Autolock是Android native层最常见的一种临界区保护手段,Autolock只是提供了一种更简便的使用Mutex的方法。
Mutex是一个C++的类,它的接口如下所示:
class Mutex {
enum { PRIVATE = 0, SHARED = 1 };
Mutex();
Mutex(const char* name);
Mutex(int type, const char* name = NULL);
~Mutex();
status_t lock();
void unlock();
status_t tryLock();
};
Mutex类有3个构造函数,最简单的函数不带任何参数。最复杂的有一个类型参数和一个名字参数,这个构造函数主要用于进程间的线程同步,这时第一个参数为“SHARED”。
Mutex的lock()和unlock()函数必须成对使用,在进入需要保护的区域前调用lock()函数,离开要保护的区域则需要调用unlock()函数。
此外,Mutex还提供了一个trylock()函数,这个函数将尝试去获取锁,如果成功则返回0,失败则返回非0值,通常是1。无论成功失败,trylock()函数都不会导致调用线程阻塞。
下面是使用Mutex来保护全局变量的一个简单例子。
Mutex g_mutex;
int g_count = 0;
void ThreadFuncA()
{
g_mutex.lock();
g_count++;
g_mutex.unlock();
}
void ThreadFuncB()
{
g_mutex.lock();
gCount--;
if(gCount >= 0) {
......
}
g_mutex.unlock();
}
使用Mutex注意不要忘记调用unlock(),否则可能会造成死锁。如果一段受保护的代码有好几个出口,每处都调用unlock()会让代码看上去不简洁,也容易因为遗忘造成错误。因此,Android封装了一个Autolock类,使用时只需要在保护代码前构造一个Autolock的局部变量就可以了。看看下面Autolock的实现代码就明白了:
class Autolock {
public:
inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
inline ~Autolock() { mLock.unlock(); }
private:
Mutex& mLock;
};
Autolock的原理很简单,就是在构造函数中加锁,在析构函数中解锁,因此,Autolock经常用在函数中来保护整个函数体。但是,Autolock使用不当也容易造成死锁。看看下面一个简单的例子:
Mutex g_lock;
void FooA()
{
AutoLock(g_lock);
.......
}
void FooB()
{
AutoLock(g_lock);
.......
FooA()
}
上面的例子中函数FooA()和FooB()都使用了AutoLock(g_lock)来进行保护,而且FooB()函数调用了FooA()。当调用FooB()函数时,就会发生死锁。当然实际的情况会更复杂,可能不是直接调用,中间会经过另外一些函数中转,这样就更不容易发现问题了。因此,不管是使用Mutex还是Autlolock都要十分小心。
下面看看Mutex类是如何实现的:
代码清单6.1 C++类Mute的实现代码
inline Mutex::Mutex() {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::Mutex(__attribute__((unused)) const char* name) {
pthread_mutex_init(&mMutex, NULL);
}
inline Mutex::Mutex(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 Mutex::~Mutex() {
pthread_mutex_destroy(&mMutex);
}
inline status_t Mutex::lock() {
return -pthread_mutex_lock(&mMutex);
}
inline void Mutex::unlock() {
pthread_mutex_unlock(&mMutex);
}
inline status_t Mutex::tryLock() {
return -pthread_mutex_trylock(&mMutex);
}
从上面的代码不难看出。Mutex类的实现只是对Linux的“pthreadmutex”系列函数进行了简单的封装而已。Linux的线程系列函数已经很完善了,没有太多需要补充的地方,只是使用稍微繁琐一点。
6.2.2 解决线程同步——条件类Condition
条件类Condition用来解决类似“生产者-消费者”类型的线程同步问题。“生产者”生产产品前先使用Mutex上锁,“消费者”则等待,“生产者”结束后,唤醒“消费者”消费产品。这类问题在多线程编程中很常见。
条件类Condition的接口如下:
class Condition {
public:
enum { PRIVATE = 0, SHARED = 1 };
enum WakeUpType {WAKE_UP_ONE = 0, WAKE_UP_ALL = 1 };
Condition();
Condition(int type);
~Condition();
status_t wait(Mutex& mutex);
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
void signal();
void signal(WakeUpType type);
void broadcast();
};
条件类需要配合Mutex来使用。Condition的构造函数和Mutex类似,也可以使用参数“SHARED”,这种情况主要用于进程间线程的同步。
Condition的wait()函数有两个,不带时间参数的函数表示无限等待;带有时间参数的wait()函数,时间超时wait()函数就会返回,不会一直等待。如果wait等待的mutex的锁释放了,所有在该mutex上等待的wait()都将返回。在mutex解锁前,还有一种方法来终止等待,那就是调用signal()函数。
signal()函数也有两个,不带参数的siganl()调用只会唤醒一个等待的线程;带参数的singal()调用可以通过参数WAKE_UP_ONE只唤醒一个等待的线程,也可通过参数WAKE_UP_ALL唤醒所有等待的线程。
broadcast()函数用来唤醒所有等待的线程。
下面是Condition类的实现代码:
代码清单6.2 C++类Condition的实现代码
inline Condition::Condition() {
pthread_cond_init(&mCond, NULL);
}
inline Condition::Condition(int type) {
if (type == SHARED) {
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&mCond, &attr);
pthread_condattr_destroy(&attr);
} else {
pthread_cond_init(&mCond, NULL);
}
}
inline Condition::~Condition() {
pthread_cond_destroy(&mCond);
}
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
inline void Condition::signal() {
pthread_cond_signal(&mCond);
}
inline void Condition::broadcast() {
pthread_cond_broadcast(&mCond);
}
和Mutex类类似,Condition类的实现也只是对Linux的“pthreadcond”系列函数进行了简单的封装。