1. Overview
操作系统的同步机制都是类似的,目前Android封装的同步类包括:
- Mutex
头文件是frameworks/native/include/utils/Mutex.h
android 中的Mutex仅仅对pthread提供的API作了简单的封装,所以函数声名和实现体都放在同一个头文件中,方便调用者操作- Condition
头文件是frameworks/native/include/utils/Condition.h
Condition是“条件变量”在android中的实现类,它实际上是依赖Mutex完成的- Barrier
头文件是frameworks/native/include/utils/Barrier.h
Barrier是同时基于Mutex和Condition实现的一个模型。AudioFlinger和SurfaceFlinger都会碰到其使用场景。
2. 进程间同步 Mutex
Mutex 头部有一个emum:
enum {
PRIVATE = 0, //只限统一进程间的同步
SHARED = 1 //支持跨进程同步
};
这代表Mutex既可以处理进程内部的同步情况,也可以处理进程间同步问题。
如果Mutex构造的时候指定它的type是SHARED的话,它是适用于跨进程共享的。
因为Mutex只有两种状态,即0或1,所以这个类只提供了三个重要接口:
// lock or unlock the mutex
status_t lock();
void unlock();
// lock if possible; returns 0 on success, error otherwise
status_t tryLock();
当调用者希望访问临界资源时,他必须现通过lock()
来获得资源锁,如果此时资源可用,则立即返回,否则进入阻塞等待,直到有人释放并唤醒它。释放资源锁调用unlock()
,同时正在等待使用这个锁的其他对象就被唤醒,然后继续执行它的任务。
trylock()
:无论能不能获取资源都不等待,而是立即返回尝试加锁的结果。若资源可用则返回成功(返回值为0);否则则返回失败(返回值不为0)。
以上三个接口的实现如下:
#if defined(HAVE_PTHREADS)
......
......
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);
}
#endif // HAVE_PTHREADS
可见Mutex其实只是基于pthread接口的再封装。
3. 条件判断:Condition
Condition通过判断条件是否满足来获得资源。
- 如果满足——马上返回,继续执行未完成的动作。
- 如果不满足——进入休眠等待,知道条件满足时有人唤醒它
Mutex与Condition的区别:
Mutex需要资源的获取者主动发起查询,当发现条件满足时获取资源,而Condition会在判断满足条件时,主动通知处于等待状态的线程。
Condition 接口源码
class Condition {
public:
enum {
PRIVATE = 0,
SHARED = 1
};
Condition();
Condition(int type);
~Condition();
// Wait on the condition variable. Lock the mutex before calling.
status_t wait(Mutex& mutex);
// same with relative timeout
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
// Signal the condition variable, allowing one thread to continue.
void signal();
// Signal the condition variable, allowing all threads to continue.
void broadcast();
private:
#if defined(HAVE_PTHREADS)
pthread_cond_t mCond;
#else
void* mState;
#endif
};
从Condition的接口函数中,可以看到:
- wait() 在等待某个条件满足,如何描述这个条件?——它提供一个黑盒的方式来让用户来设定何种条件满足。
- 为什么需要mutex?—— Barrier中提到原因,是由于condition自身的不完整性造成的。
4. Barrier
Barrier中文名为“栅栏,障碍”,它是一个填充了具体条件的Condition
Barrier是专门为SurfaceFlinger而设计的,并不是像Mutex,Condition一样作为常用的Utility提供给整个Android系统使用。
Barrier接口源码
class Barrier
{
public:
inline Barrier() : state(CLOSED) { }
inline ~Barrier() { }
// Release any threads waiting at the Barrier.
// Provides release semantics: preceding loads and stores will be visible
// to other threads before they wake up.
void open() {
Mutex::Autolock _l(lock);
state = OPENED;
cv.broadcast();
}
// Reset the Barrier, so wait() will block until open() has been called.
void close() {
Mutex::Autolock _l(lock);
state = CLOSED;
}
// Wait until the Barrier is OPEN.
// Provides acquire semantics: no subsequent loads or stores will occur
// until wait() returns.
void wait() const {
Mutex::Autolock _l(lock);
while (state == CLOSED) {
cv.wait(lock);
}
}
private:
enum { OPENED, CLOSED };
mutable Mutex lock;
mutable Condition cv;
volatile int state;
};
Barrier 提供了三个接口函数:wait()
, close()
, open()
其condition就是 state,有OPENED和CLOSED两个状态;上面三个函数都是先获取一个Mutex锁,以保证对state资源的占有是互斥使用的,然后再调用Condition中的wait()函数。
Condition中的wait函数实现:
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
pthread_cond_wait()
函数的逻辑语义如下
- 释放mutex锁
- 进入休眠等待
- 唤醒后再获取mutex锁
释放再唤醒mutex锁的操作,是为了让其它进程在open()/close()的时候可以获取到state资源,以免造成死锁。
wait()函数结尾会自动释放mutex锁,因为_l是一个AutoLock,也就是说,在等待结束时,程序会结束针对共享资源的持锁了。如果后续再有对共享资源的操作,应该重新再加锁。
因为以上特性,Barrier通常被用于线程初始化的判断,这种场景具有不可逆性,也就是说,被初始化之后,后期不可能再出现“没有初始化”的情况了,这时wait()结束后不再持锁,也是安全的了。
5.Autolock:加解锁自动化操作
Autolock时Mutex里的一个嵌套类,实现代码:
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构造时,主动调用内部成员变了mLock的lock()函数来获取一个锁,而析构的时候,正好相反,调用unlock(),这样当他生命周期结束的时候就会自动解开对资源的锁。
6. ReaderWriterMutex:读写锁
Android Art 虚拟机中用到互斥和锁操作的地方非常多,为此它实现了一整套自己的mutex机制,可以参考art/runtime/base 目录了解。
ReaderWriterMutex是art虚拟机中一个特殊的mutex,它是“读写锁”的意思,它提供了一下接口:
class LOCKABLE ReaderWriterMutex : public BaseMutex {
public:
// Block until ReaderWriterMutex is free then acquire exclusive access.
void ExclusiveLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION();
void WriterLock(Thread* self) EXCLUSIVE_LOCK_FUNCTION() {
ExclusiveLock(self); }
// Release exclusive access.
void ExclusiveUnlock(Thread* self) UNLOCK_FUNCTION();
void WriterUnlock(Thread* self) UNLOCK_FUNCTION() { ExclusiveUnlock(self); }
// Block until ReaderWriterMutex is shared or free then acquire a share on the access.
void SharedLock(Thread* self) SHARED_LOCK_FUNCTION();
void ReaderLock(Thread* self) SHARED_LOCK_FUNCTION() { SharedLock(self); }
// Release a share of the access.
void SharedUnlock(Thread* self) UNLOCK_FUNCTION();
void ReaderUnlock(Thread* self) UNLOCK_FUNCTION() { SharedUnlock(self); }
}
从上面的接口可以看到,art中的的Exclusive和Shared分别代表的是write和read权限,也可以看出:它可以允许有多个对象共享Read锁,但只能有唯一的对象持有Write锁。
ReaderWriterMutex有以下三种状态:
- Free: 没有被任何对象持有
- Exclusive: 当前被唯一对象持有
- Shared: 被多个对象持有。