Mutex类使用Futex实现同步

新书上市《深入解析Android 5.0系统》

 以下内容节选自本书


下面我们将通过一个实际的例子来进一步的了解Futex的用法。前面我们介绍Bionic中的线程管理时介绍了pthread的临界区函数。临界区的实现中最重要的是上锁和解锁函数,下面我们看看它们的实现:

1.       Bionic的实现中,pthread_mutex_lock()调用了内部函数_normal_lock()来实现上锁功能,代码如下:

static__inline__ void _normal_lock(pthread_mutex_t*  mutex, intshared)

{

   constint unlocked = shared | MUTEX_STATE_BITS_UNLOCKED;

const int locked_uncontended = shared |MUTEX_STATE_BITS_LOCKED_UNCONTENDED;

    if(__bionic_cmpxchg(unlocked, locked_uncontended,&mutex->value) != 0) {

       constint locked_contended = shared | MUTEX_STATE_BITS_LOCKED_CONTENDED;

       while(__bionic_swap(locked_contended, &mutex->value) !=unlocked)

           __futex_wait_ex(&mutex->value,shared, locked_contended, 0);

   }

   ANDROID_MEMBAR_FULL();

}

_normal_lock()函数调用原子操作函数__bionic_cmpxchg()来检测mutex->value的值是否等于unlocked变量的值(unlocked代表未锁定的状态),如果相等则将mutex->value的值设置为locked_uncontended变量的值(表示锁定但是没有竞争的状态)。__bionic_cmpxchg()函数在mutex->value不等于unlocked的情况下会返回非0,这样就进入if语句内执行。

__bionic_swap()函数会将mutex->value的值设为locked_contended(表示有竞争),同时返回mutex->value原来的值。如果mutex->value原来的值不等于unlocked则调用函数__futex_wait_ex()挂起线程等待。这里使用while循环是因为可能有多个线程在等待,即使从挂起状态恢复后也可能还是抢不到锁,所以要重新进入等待状态。

2.       pthread_mutex_unlock()则调用了内部函数_normal_unlock()来实现解锁功能。代码如下所示:

static__inline__ void _normal_unlock(pthread_mutex_t*  mutex, intshared)

{

   ANDROID_MEMBAR_FULL();

if (__bionic_atomic_dec(&mutex->value)!=

             (shared|MUTEX_STATE_BITS_LOCKED_UNCONTENDED)){

       mutex->value= shared;

       __futex_wake_ex(&mutex->value,shared, 1);

   }

}

解锁时首先使用__bionic_atomic_dec()函数对mutex->value执行减一操作,函数__bionic_atomic_dec()将返回mutex->value原来的值。如果mutex->value原来的值不是锁定但是无竞争的状态,也就意味着还有线程在等待,所以要调用__futex_wake_ex()来唤醒等待的线程。

Mutex的实现代码中我们应该能体会到Futex的优点,如果多个线程访问临界区的时间是错开的,也就是没有真正的竞争发生,那么实际上是不会产生系统调用的。

当然如果多个线程在竞争临界区的锁,还是一样会有挂起和唤醒的系统调用。这种情况下 Futex 的效率和以前的实现就没有区别了。


你可能感兴趣的:(Mutex类使用Futex实现同步)