C++知识

Android系统中的同步机制

2018-11-02  本文已影响0人  Crane_FeiE

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()函数的逻辑语义如下

  1. 释放mutex锁
  2. 进入休眠等待
  3. 唤醒后再获取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: 被多个对象持有。
上一篇下一篇

猜你喜欢

热点阅读