美文网首页
C中的线程锁,以及android的懒人封装

C中的线程锁,以及android的懒人封装

作者: 浪里_个郎 | 来源:发表于2020-03-30 17:02 被阅读0次

在java中,我们通常通过volitile、synchronized关键字来保证变量、函数或代码段在多线程中数据的原子性。在使用C进行linux编程时,我们需要用到以下函数:

//互斥锁上锁
pthread_mutex_lock
//互斥锁解锁
pthread_mutex_unlock
//动态创建条件变量
pthread_cond_init
//等待条件变量,挂起线程,区别是后者,会有timeout时间,
//如果到了timeout,线程自动解除阻塞,这个时间和 time()系统调用相同意义的。以1970年时间算起
pthread_cond_wait / pthread_cond_timedwait
//激活等待列表中的线程,
pthread_cond_signal
//激活所有等待线程列表中最先入队的线程
pthread_cond_broadcast
//销毁互斥锁
pthread_mutex_destroy
//销毁条件锁
pthread_cond_destroy

下面我们来总结不同场景下要怎么使用上面的函数。

最普通的阻塞锁

pthread_mutex_t mutex_;
pthread_mutex_init(&mutex_, NULL);
pthread_mutex_lock(&mutex_);
//这里执行需要多线程同步的代码
...
pthread_mutex_unlock(&mutex_);
pthread_mutex_destroy(&mutex_);

带条件等待的锁

pthread_mutex_t mutex_;
pthread_mutex_init(&mutex_, NULL);
pthread_cond_t cond_;
pthread_cond_init(&cond_, NULL);
//~~~~~A函数~~~~~~~~~
pthread_mutex_lock(&mutex_);
//这里执行需要多线程同步的代码
...
//等待唤醒
pthread_cond_wait(&cond_, &mutex_);
pthread_mutex_unlock(&mutex_);
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&mutex_);

//~~~~~B函数~~~~~~~~~
//唤醒A函数的pthread_cond_wait,使其继续往下执行
pthread_cond_signal(&cond_);

需要注意,条件等待锁pthread_cond_wait需要配合pthread_mutex_lock使用!因为多个线程同时请求pthread_cond_wait会产生死锁。
调用pthread_cond_wait后,会首先对入参的pthread_mutex_t执行解锁,以使其他线程可以进行操作,最终使得条件成立,解除条件锁定。同时还会对pthread_cond_t加锁,此时线程挂起不占用CPU周期。当通过pthread_cond_signal对pthread_cond_t解锁后,pthread_cond_wait函数又会对pthread_mutex_t执行加锁!简而言之,这个函数会涉及解锁-等待条件信号-加锁三个过程。

android对线程锁的懒人封装

在system/core/include/utils/Mutex.h中定义:

typedef Mutex::Autolock AutoMutex;

Mutex和Autolock同样在Mutex.h中,定义如下:

class Mutex {
public:
    enum {
        PRIVATE = 0,
        SHARED = 1
    };

                Mutex();
    explicit    Mutex(const char* name);
    explicit    Mutex(int type, const char* name = NULL);
                ~Mutex();

    // lock or unlock the mutex
    status_t    lock();
    void        unlock();

    // lock if possible; returns 0 on success, error otherwise
    status_t    tryLock();

#if defined(__ANDROID__)
    // Lock the mutex, but don't wait longer than timeoutNs (relative time).
    // 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.
    //
    // FIXME?: pthread_mutex_timedlock is based on CLOCK_REALTIME,
    // which is subject to NTP adjustments, and includes time during suspend,
    // so a timeout may occur even though no processes could run.
    // Not holding a partial wakelock may lead to a system suspend.
    status_t    timedLock(nsecs_t timeoutNs);
#endif

    // Manages the mutex automatically. It'll be locked when Autolock is
    // constructed and released when Autolock goes out of scope.
    class Autolock {
    public:
        //构造时加锁
        inline explicit Autolock(Mutex& mutex) : mLock(mutex)  { mLock.lock(); }
        inline explicit Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
        //析构时解锁
        inline ~Autolock() { mLock.unlock(); }
    private:
        Mutex& mLock;
    };

private:
    friend class Condition;

    // A mutex cannot be copied
                Mutex(const Mutex&);
    Mutex&      operator = (const Mutex&);

#if !defined(_WIN32)
    pthread_mutex_t mMutex;
#else
    void    _init();
    void*   mState;
#endif
};
//构造时初始化同步锁
inline Mutex::Mutex() {
    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);
}

结合上面的代码,说说为什么Autolock是懒人封装:
首先Mutex的构造和析构帮我们完成了同步锁初始化和销毁,而Autolock的构造和析构进一步帮我们完成了加锁和解锁的过程!我们知道,对于函数中的局部变量来说,函数执行完成,就会被销毁,所以,我们可以这样通过Autolock实现线程同步:

void func() {
  //锁初始化
  Mutex mLock;
  //加锁
  AutoMutex _l(mLock);
  //这里执行需要多线程同步的代码
  ...
}

我们不用再手动调用pthread_mutex相关初始化、加锁解锁和销毁函数了,偷懒不。
注意,mLock可以定义成成员变量重复使用,但AutoMutex 的对象必须是局部变量

android对条件锁的懒人封装

既然偷懒,那就懒到底。android同样对pthread_cond相关函数进行了封装,在/system/core/include/utils/Condition.h
因为封装的方法和Autolock基本一致,这里就不贴代码了,使用实例如下:

class Barrier
{
public:
    inline Barrier() : state(CLOSED) { }//state就是所谓的“条件”
    inline ~Barrier() { }
    void open() {
        Mutex::Autolock _l(lock);
        state = OPENED;
        cv.broadcast();
    }
    void close() {
        Mutex::Autolock _l(lock);
        state = CLOSED;
    }
    void wait() const {
        Mutex::Autolock _l(lock);//临时对象_l,用lock来构造,在AutoLock的构造函数里已给lock加锁(调用lock()函数)——该wait()函数执行完毕,会自动释放lock(这个场景会使得其他线程再次修改state,产生不安全因素。不过由于Barrier的使用场景的特殊性,其用在线程初始化时,故OK。)
        while (state == CLOSED) {//循环,直到state==OPENED
            cv.wait(lock);
        }
    }
private:
    enum { OPENED, CLOSED };
    mutable     Mutex       lock;//持有一个互斥锁
    mutable     Condition   cv;//持有一个条件变量
    volatile    int         state;//每次都从内存更新的“条件”
};

Condition和Mutex一样,可以定义为成员变量重复使用。

相关文章

网友评论

      本文标题:C中的线程锁,以及android的懒人封装

      本文链接:https://www.haomeiwen.com/subject/vmpmuhtx.html