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

在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一样,可以定义为成员变量重复使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 本篇主要讲Linux环境下的多线程同步内核对象。 (1)linux线程同步之互斥体:linux互斥体的用法与win...
    菠落箩落萝阅读 644评论 0 0
  • Q:为什么出现多线程? A:为了实现同时干多件事的需求(并发),同时进行着下载和页面UI刷新。对于处理器,为每个线...
    幸福相依阅读 1,576评论 0 2
  • 多线程系列文章源码头文件内容: #include #include #include 作为程序员,就是要减少重复劳...
    batbattle阅读 921评论 0 1
  • OSSpinLock OSSpinLock 不再安全,主要原因发生在低优先级线程拿到锁时,高优先级线程进入忙等(b...
    GAME666阅读 1,285评论 0 0
  • 1. 什么情况下会有线程隐患? 我们在使用多线程技术带来的便利的同时,也需要考虑下多线程所带来的隐患。比如,我们可...
    沉江小鱼阅读 812评论 0 11