文章目录

  • 1.条件变量
    • 1.1 条件变量函数:
    • 1.2 pthread_cond_init 函数
    • 1.3 pthread_cond_destroy 函数
    • 1.4 pthread_cond_wait 函数
    • 1.5 pthread_cond_timedwait 函数
    • 1.6 pthread_cond_signal 函数
    • 1.7 pthread_cond_broadcast 函数
  • 2.生产者消费者模型
  • 3.条件变量的优点:
  • 4.信号量
  • 5.信号量函数:
    • 5.1 信号量基本操作:
    • 5.2 sem_init 函数
    • 5.3 sem_destroy 函数
    • 5.4 sem_wait 函数
    • 5.5 sem_post 函数
    • 5.6 sem_trywait 函数
    • 5.7 sem_timedwait 函数
  • 6.信号量举例
  • 7.生产者消费者信号量模型
  • 8.哲学家吃饭问题
  • 9.共享内存

1.条件变量

条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

1.1 条件变量函数:

pthread_cond_init 函数
pthread_cond_destroy 函数
pthread_cond_wait 函数
pthread_cond_timedwait 函数
pthread_cond_signal 函数
pthread_cond_broadcast 函数
以上 6 个函数的返回值都是:成功返回 0, 失败直接返回错误号。
pthread_cond_t 类型 用于定义条件变量
pthread_cond_t cond;

1.2 pthread_cond_init 函数

初始化一个条件变量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
参 2:attr 表条件变量属性,通常为默认值,传 NULL 即可
也可以使用静态初始化的方法,初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

1.3 pthread_cond_destroy 函数

销毁一个条件变量

int pthread_cond_destroy(pthread_cond_t *cond);

1.4 pthread_cond_wait 函数

阻塞等待一个条件变量

 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

函数作用:

  1. 阻塞等待条件变量 cond(参 1)满足
  2. 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex) ,1.2.两步为一个原子操作。
  3. 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);

1.5 pthread_cond_timedwait 函数

限时等待一个条件变量

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
参 3: 参看 man sem_timedwait 函数,查看 struct timespec 结构体。
struct timespec {time_t tv_sec; /* seconds */ 秒long tv_nsec; /* nanosecondes*/ 纳秒
}
形参 abstime:绝对时间。
如:time(NULL)返回的就是绝对时间。而 alarm(1)是相对时间,相对当前时间定时 1 秒钟。struct timespec t = {1, 0};
pthread_cond_timedwait (&cond, &mutex, &t); 只能定时到 1970 年 1 月 1 日 00:00:01 秒(早已经过去)正确用法:
time_t cur = time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec = cur+1; 定时 1 秒
pthread_cond_timedwait (&cond, &mutex, &t); 传参 参 APUE.11.6 线程同步条件变量小节
在讲解 setitimer 函数时我们还提到另外一种时间类型:
 struct timeval {time_t tv_sec; /* seconds */ 秒suseconds_t tv_usec; /* microseconds */ 微秒};

1.6 pthread_cond_signal 函数

唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_signal(pthread_cond_t *cond);

1.7 pthread_cond_broadcast 函数

唤醒全部阻塞在条件变量上的线程

 int pthread_cond_broadcast(pthread_cond_t *cond);

2.生产者消费者模型

线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。
假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。
两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct msg {struct msg *next;int num;
};
struct msg *head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{struct msg *mp;for (;;) {pthread_mutex_lock(&lock);while (head == NULL) { //头指针为空,说明没有节点 可以为 if 吗pthread_cond_wait(&has_product, &lock);}mp = head; head = mp->next; //模拟消费掉一个产品pthread_mutex_unlock(&lock);printf("-Consume ---%d\n", mp->num);free(mp);sleep(rand() % 5);}
}void *producer(void *p)
{struct msg *mp;while (1) {mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1; //模拟生产一个产品printf("-Produce ---%d\n", mp->num);pthread_mutex_lock(&lock);mp->next = head;head = mp;pthread_mutex_unlock(&lock);pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程唤醒sleep(rand() % 5);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;srand(time(NULL));pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_cond
-Produce ---425
-Consume ---425
-Produce ---486
-Consume ---486
-Produce ---699
-Consume ---699
-Produce ---736
-Consume ---736
-Produce ---452
-Consume ---452
-Produce ---529
-Consume ---529
-Produce ---664
-Consume ---664
-Produce ---340
-Consume ---340
^C

3.条件变量的优点:

相较于 mutex 而言,条件变量可以减少竞争。
如直接使用 mutex,除了生产者、消费者之间要竞争互斥量以外,
消费者之间也需要竞争互斥量,但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。
有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。

4.信号量

进化版的互斥锁(1 --> N)
由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,
使用互斥锁是没有办法实现的,只能将整个数据对象锁住。
这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。
线程从并行执行,变成了串行执行。与直接使用单进程无异。
信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。

5.信号量函数:

sem_init 函数
sem_destroy 函数
sem_wait 函数
sem_trywait 函数
sem_timedwait 函数
sem_post 函数
以上 6 个函数的返回值都是:成功返回 0, 失败返回-1,同时设置 errno。(注意,它们没有 pthread 前缀)
sem_t 类型,本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。
sem_t sem; 规定信号量 sem 不能 < 0。头文件 <semaphore.h>

5.1 信号量基本操作:

sem_wait: 1. 信号量大于 0,则信号量-- (类比 pthread_mutex_lock)2. 信号量等于 0,造成线程阻塞
sem_post: 将信号量++,同时唤醒阻塞在信号量上的线程 (类比 pthread_mutex_unlock)
但,由于 sem_t 的实现对用户隐藏,所以所谓的++、--操作只能通过函数来实现,而不能直接++、--符号。
信号量的初值,决定了占用信号量的线程的个数。

5.2 sem_init 函数

初始化一个信号量

int sem_init(sem_t *sem, int pshared, unsigned int value);
参 1:sem 信号量
参 2:pshared 取 0 用于线程间;取非 0(一般为 1)用于进程间
参 3:value 指定信号量初值

5.3 sem_destroy 函数

销毁一个信号量

int sem_destroy(sem_t *sem);

5.4 sem_wait 函数

给信号量加锁 –

int sem_wait(sem_t *sem);

5.5 sem_post 函数

给信号量解锁 ++

int sem_post(sem_t *sem);

5.6 sem_trywait 函数

尝试对信号量加锁 – (与 sem_wait 的区别类比 lock 和 trylock)

int sem_trywait(sem_t *sem);

5.7 sem_timedwait 函数

限时尝试对信号量加锁 –

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
参 2:abs_timeout 采用的是绝对时间。
定时 1 秒:
time_t cur = time(NULL); 获取当前时间。
struct timespec t; 定义 timespec 结构体变量 t
t.tv_sec = cur+1; 定时 1 秒
t.tv_nsec = t.tv_sec +100;
sem_timedwait(&sem, &t); 传参

6.信号量举例

#include<stdio.h>
#include <semaphore.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>sem_t sem;
void *func(void* arg){int i=(int)arg;while(1){sem_wait(&sem);printf("我是%d,我正在吃饭,当前座位剩余:%d\n",i,sem);sleep(2);sem_post(&sem);sleep(4);}return NULL;
}int main()
{pthread_t tid[10];sem_init(&sem,0,5);int i;for(i=0;i<10;i++){pthread_create(&tid[i],NULL,func,(void*)i);}for(i=0;i<10;i++){pthread_join(tid[i],NULL);}sem_destroy(&sem);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./sem
我是0,我正在吃饭,当前座位剩余:4
我是1,我正在吃饭,当前座位剩余:3
我是2,我正在吃饭,当前座位剩余:2
我是3,我正在吃饭,当前座位剩余:1
我是4,我正在吃饭,当前座位剩余:0
我是5,我正在吃饭,当前座位剩余:1
我是8,我正在吃饭,当前座位剩余:2
我是6,我正在吃饭,当前座位剩余:0
我是7,我正在吃饭,当前座位剩余:1
我是9,我正在吃饭,当前座位剩余:0
我是0,我正在吃饭,当前座位剩余:4
我是4,我正在吃饭,当前座位剩余:2
我是1,我正在吃饭,当前座位剩余:3
我是3,我正在吃饭,当前座位剩余:1
我是2,我正在吃饭,当前座位剩余:0
^C

7.生产者消费者信号量模型

使用信号量完成线程间同步,模拟生产者,消费者问题。

【sem_product_consumer.c】
规定: 如果□中有数据,生产者不能生产,只能阻塞。如果□中没有数据,消费者不能消费,只能等待数据。定义两个信号量:S 满 = 0, S 空 = 1 (S 满代表满格的信号量,S 空表示空格的信号量,程序起始,格子一定为空)

所以有:

T 生产者主函数 { sem_wait(S 空); //空格减1生产....sem_post(S 满); //大饼加1
}
T 消费者主函数 {sem_wait(S 满);//大饼减1消费....sem_post(S 空);//空格加1}
假设: 线程到达的顺序是:T 生、T 生、T 消。
那么: T 生 1 到达,将 S 空-1,生产,将 S 满+1
T 生 2 到达,S 空已经为 0, 阻塞
T 消 到达,将 S 满-1,消费,将 S 空+1三个线程到达的顺序是:T 生 1、T 生 2、T 消。而执行的顺序是 T 生 1、T 消、T 生 2
这里,S 空 表示空格子的总数,代表可占用信号量的线程总数-->1。其实这样的话,信号量就等同于互斥锁。
但,如果 S 空=2、3、4……就不一样了,该信号量同时可以由多个线程占用,不再是互斥的形式。
因此我们说信号量是互斥锁的加强版。

8.哲学家吃饭问题

让所有哲学家听到吃饭口令之后,都拿自己右手边的筷子,但是选定某一个哲学家A必须拿自己左手边的筷子;
这样子,A哲学家右边的哲学家B肯定可以第一个吃到饭,吃完饭放下筷子,下面一个人就可以吃饭了;
以此类推。。。

9.共享内存

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>int a=10;
void* func(void* arg)
{sleep(1);a=100;printf("我要修改a\n");sleep(2);return NULL;
}
int main()
{pthread_t tid;pthread_create(&tid,NULL,func,NULL);while(1){printf("%d\n",a);sleep(1);}pthread_join(tid,NULL);return 0;
}
zhaoxr@zhaoxr-ThinkPad-E450:~/pthread$ ./pthread_com
10
我要修改a
100
100
100
100
100
100
100
^C

linux——线程通信(2)相关推荐

  1. C++多线程:Linux 线程通信,唤醒,互斥锁(未完待续)

    c++ multi thread message sending and notify 线程通信常用的方法有共享内存和消息传递,推荐使用消息传递. 最常用的就是管道了,可以使用匿名管道或者命名管道. ...

  2. linux——线程通信(1)

    文章目录 1.同步概念 2.线程同步 3.数据混乱原因: 4.互斥量 mutex 5.mutex 主要应用函数: 5.1 pthread_mutex_init 函数 5.2 pthread_mutex ...

  3. Linux 线程与进程,以及通信

    http://blog.chinaunix.net/uid-25324849-id-3110075.html 部分转自:http://blog.chinaunix.net/uid-20620288-i ...

  4. linux进程同步/通信,线程同步/通信的区别

    大家常问的是线程的同步和进程的通信,较少问线程通信和进程同步的,那它们有什么区别呢?其实它们没啥区别,线程同步和线程通信其实就是一回事,进程通信和进程同步也是一回事,不要被这些搞混了. linux线程 ...

  5. linux线程间通信优点,进程间通信与线程间通信【转】

    一个进程写管道:写入字节数小于PIPE_BUF是原子操作,写操作在管道缓冲区没有及时读走时发生阻塞. 一个进程读管道:读操作在管道缓冲区没有数据时发生阻塞. 以前一直想找个机会总结一下进程和线程的通信 ...

  6. Linux下c开发 之 线程通信

    Linux下c开发 之 线程通信 1.Linux"线程" 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种"多进程单线 ...

  7. Linux下c开发 之 线程通信与pthread_cond_wait()的使用

    pthread_cond_wait() /************pthread_cond_wait()的使用方法**********/ pthread_mutex_lock(&qlock); ...

  8. Linux下c开发 之 线程通信(转)

    1.Linux"线程" 进程与线程之间是有区别的,不过Linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种"多进程单线程"的操作系统.Linu ...

  9. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

最新文章

  1. 加工中心宏程序生成器_零件行外球面加工,老师傅告诉你,普通程序与宏加工哪个更方便...
  2. python下的橡皮线_python线性代数常用操作
  3. Expandable Input Toolbar
  4. 老司机又开车(来不及解释,快上!)之秒懂单片机指针!
  5. onblur 对象失去焦点事件
  6. 解决Firefox已阻止运行早期版本Adobe Flash
  7. 【Java从入门到头秃专栏 4】语法篇(三) :字符串 数组
  8. CV之Haar特征描述算子-人脸检测
  9. 时间管理----分析工作安排和首要任务
  10. c语言字符串提取第二个字符,c语言如何复制字符串(取前n个字符)strncpy()函数的应用实例...
  11. python入门100例题-这 100 道 Python 题,拿去刷!!!
  12. 普元BPS:唯一零错误的流程平台
  13. 小米6刷android 8.0,小米小米6(安卓8.0)手机快速救砖,线刷教程分享,小白轻松救活手机...
  14. 政府安全资讯精选 2017年第四期:聚焦美国网络安全新动态
  15. linux hba卡超时时间,如何设置Emulex HBA 卡超时参数
  16. 一些免费在线杀毒网址
  17. 如何在个人信用报告中添加“本人声明”?
  18. 微控制器编程技术c语言,1.单片机C语言编程技术分析.pptx
  19. 用计算机打有一群人去喝酒的游戏,适合聚会时玩的40个小游戏
  20. 蓝牙耳机什么牌子的好?口碑、销量双高的十大蓝牙耳机品牌!

热门文章

  1. LeetCode 751. IP 到 CIDR(贪心)
  2. LeetCode 1451. 重新排列句子中的单词(桶排序)
  3. LeetCode 690. 员工的重要性(图的DFSBFS)
  4. python中的进程池Pool
  5. Linux 文件基本属性以及操作技巧
  6. extjs2.0 ie8 下拉树_ExtJs下拉树的实现
  7. redis如何设置定时过期_redis补充6之Redis 设置过期时间
  8. ICML2021 | Self-Tuning: 如何减少对标记数据的需求?
  9. 【面试必备】奉上最通俗易懂的XGBoost、LightGBM、BERT、XLNet原理解析
  10. 美团外卖客户端高可用建设体系