pthread.h 相关函数使用方法集锦之线程同步变量

前言

pthread(POSIX thread),简称为pthread,是线程的POSIX标准,在类Unix操作系统中(Unix、Linux、Mac OS X等),都是用pthread作为操作系统的线程。<pthread.h>作为其编程标准的头文件,本文探讨里面的常用函数意义以及使用方法。

在多线程编程中,操作系统引入了锁机制。通过锁机制,能够保证在多核多线程环境中,在某一个时间点上,只能有一个线程进入临界区代码,从而保证临界区中操作数据的一致性。

在pthread里面实现了5中同步变量,分别是互斥锁(mutex)、自旋锁(spinlock)、条件变量(condition)、读写锁(rwlock)以及栅栏(barrier)。

线程同步变量

pthread互斥锁(mutex)

互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁在逻辑上绑定(要申请该资源必须先获取锁)。

访问公共资源前,必须申请该互斥锁,若处于开锁状态,则申请到锁对象,并立即占有该锁,以防止其他线程访问该资源;如果该互斥锁处于锁定状态,则阻塞当前线程。

只有锁定该互斥锁的进程才能释放该互斥锁,其他线程试图释放无效。互斥锁使用的非常广泛,在所有线程同步变量中,可以重点关注。

pthread_mutex_t

锁类型,定义互斥锁。

pthread_mutex_init

初始化互斥锁。

pthread_mutex_lock

申请互斥锁并占有互斥锁,其他线程无法访问该资源。

pthread_mutex_trylock

pthread_mutex_unlock

释放互斥锁,此时其他线程可以访问该资源。

pthread_mutex_destroy

释放锁资源。

pthread条件变量(condition)

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

pthread_cond_t

pthread_cond_init

pthread_cond_signal

pthread_cond_wait

pthread_cond_broadcast

pthread_cond_wait的使用

pthread_cond_wait总是和一个互斥量同时使用的,我们看看APUE中关于pthread_cond_wait使用的原话:

传递给pthread_cond_wait的互斥量对条件(condition)进行保护。调用者把锁住的互斥量传给函数,函数然后自动把调用线程放在等待条件(condition)的线程列表上,对互斥量进行解锁。这就关闭了条件检查线程进入休眠状态等待条件改变这两个操作之间的时间通道,线程就不会错过条件(condition)的任何变化。

这段话说明了为什么需要互斥量来保护条件变量,因为条件检查线程进入休眠状态等待条件改变这两个操作都不是线程安全的,所以需要互斥量进行保护。

pthread_mutex_lock(&mutex);
pthread_cond_wait(&condition, &mutex);
pthread_mutex_unlock(&mutex);

我们再来看看互斥量传参进入pthread_cond_wait时,pthread_cond_wait对互斥量做了什么操作。再来回味一下上面APUE的这段话:

调用者把锁住的互斥量传给函数,函数然后自动把调用线程放在等待条件(condition)的线程列表上,对互斥量进行解锁

也就是pthread_cond_wait里面做了两个操作,第一个操作是将调用线程放在等待条件的线程列表上;第二个操作是将互斥量解锁

注意,将互斥量解锁以后pthread_cond_wait并没有返回,而是等待条件成立,等条件成立时,pthread_cond_wait会再次获得互斥量。

所以pthread_cond_wait对互斥量的操作实际上是这样子的。

| -- pthread_mutex_lock lock 互斥量          (1)
|
| -- 进入pthread_cond_wait函数逻辑
| -- unlock 互斥量                           (2)
|
| -- 等待条件成立
|
| -- lock 互斥量                             (3)
|
| -- 退出pthread_cond_wait函数逻辑
|
| -- pthread_mutex_unlock unlock 互斥量      (4)

在这个过程中,互斥量实际上是已经被锁住了两次。

我们编写一个死锁的例子举证一下上面的过程,在这个例子我们验证pthread_cond_wait是否会lock互斥量,进入pthread_cond_wait时,互斥量是没有锁住的,在pthread_cond_wait返回之后,别的线程尝试获取锁,如果卡死,那便说明pthread_cond_wait里面有lock互斥量的操作。

#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* test(void *p){
    pthread_mutex_lock(&mutex);  //  (1)
    pthread_mutex_unlock(&mutex); // (4)
    cout << "child thread wait condition. " << endl;
    pthread_cond_wait(&cond, &mutex); // (2) (3)
    cout << "child thread get condition. " << endl;
    cout << "child thread return." << endl;
    // pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, test, NULL);
    sleep(1); // 保证子线程先启动
    cout << "main thread signal condition. " << endl;
    pthread_cond_signal(&cond);
    sleep(1); // 保证子线程能够先得到收到条件并处理完
     cout << "main thread acquire lock. " << endl;
    pthread_mutex_lock(&mutex); // 尝试获取锁
    cout << "main thread acquire lock success. " << endl;
}

/* =========================================================================
 * 输出
 *
 * child thread wait condition.
 * main thread signal condition.
 * child thread get condition.
 * child thread return.
 * main thread acquire lock.
 *
 * =========================================================================
 */

在子线程返回后,主线程会一直卡在acquire lock这里,不会success,说明在pthread_cond_wait里面确实有加锁的操作,此时如没有释放锁,其他线程获取锁将会进入死锁状态,所以在pthread_cond_wait使用前后,一定要使用pthread_mutex_lock和pthread_mutex_unlock。

pthread自旋锁(spinlock)

自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

pthread_spinlock_t

pthread_spin_init

pthread_spin_destroy

pthread_spin_lock

pthread_spin_trylock

pthread_spin_unlock

pthread读写锁(rwlock)

读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者

pthread_rwlock_t

pthread_rwlock_init

pthread_rwlock_wrlock

pthread_rwlock_rdlock

pthread_rwlock_unlock

pthread_rwlock_tryrdlock

pthread_rwlock_trywdlock

pthread_rwlock_destroy

pthread栅栏(barrier)

栅栏(Barrier)是并行计算中的一种同步方法。对于一群进程或线程,程序中的一个同步屏障意味着任何线程/进程执行到此后必须等待,直到所有线程/进程都到达此点才可继续执行下文。

pthread_barrier_t

pthread_barrier_init

pthread_barrier_wait

pthread_barrier_destroy

原文链接

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

推荐阅读更多精彩内容

  • linux编程-线程 MUTEX 一.概述 互斥量是线程同步的一...
    Aska偶阵雨阅读 479评论 0 0
  • Q:为什么出现多线程? A:为了实现同时干多件事的需求(并发),同时进行着下载和页面UI刷新。对于处理器,为每个线...
    幸福相依阅读 1,578评论 0 2
  • 线程 在linux内核那一部分我们知道,线程其实就是一种特殊的进程,只是他们共享进程的文件和内存等资源,无论如何对...
    大雄good阅读 668评论 0 2
  • 线程 线程的概念 典型的UNIX进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事。有了多个控制线程后,在...
    ColdWave阅读 1,458评论 0 0
  • 1,2017年1月21日开始吃地瓜餐,约20日后开始肤色发黄,当时吃的黄的多!65天停后,肤色渐渐变回來了!中间黄...
    南得糊涂呀阅读 992评论 0 1