makefile ifneq多个判断条件_线程同步图解 条件变量

点击上方蓝字,关注更多优质原创

// 这是断点实验室的第6篇原创

线程同步问题广泛应用于多种场景,特别是与网络数据收发等耗时操作有关的场景。线程的操作往往比较抽象,线程大多运行在程序的后台,无法直观的查看其运行状态,因此,本文以图解的形式,为读者讲述线程同步的原理,并附以相关例程方便大家调试。 本文讲述一个经典的生产者-消费者线程同步模型,用于描述与后台缓存队列相关线程的同步过程。线程模型以互斥量(pthread_mutex_t)、条件变量为基础(pthread_cond_t),讲述互斥量如何与条件变量相互配合,以生产者-消费者的方式共同维护临界区资源1、生产者-消费者线程模型

本文主要讨论posix标准下的生产者-消费者线程模型,posix标准多用于类linux相关环境

POSIX: The Portable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. POSIX defines the application programming interface (API), along with command line shells and utility interfaces, for software compatibility with variants of Unix and other operating systems
1.1 线程模型工作原理

生产者-消费者(producer-consumer)问题是一个经典的线程同步问题,它可以描述为两个或者多个线程共同维护同一个临界区资源(critical resource),其中,生成者线程负责(例如从网络接口中抽取数据等)向临界区注入数据,消费者线程负责从临界区抽取数据,并对数据进行处理

下图为生产者-消费者线程同步模型示意图。

e9f7cbb1eaae9579b22f226db754b765.png

图中上面的process_msg为消费者线程,下面的enqueue_msg为生产者线程,从左到右代表了线程执行的时间线。

我们先来看消费者线程,消费者线程率先获取互斥锁对象(图中的红点表示互斥锁对象),获得了对临界区资源的独占处理权及cpu资源的优先使用权,然后开始执行自己的线程函数。

在消费者的线程函数中,首先检查临界区资源是否满足执行条件,若满足执行条件,则从队列中取出数据执行自己的逻辑。

如果临界区资源不满足执行条件,如队列为空,此时,消费者线程通过在pthread_cond_wait中临时释放互斥锁,并将自己投入休眠状态,等待被生产者线程向临界区注入数据,将自己唤醒并重新获得互斥锁,这时消费者线程会阻塞在pthread_cond_wait调用中

消费者线程通过在pthread_cond_wait中临时释放互斥锁后,将自己投入休眠状态,此时生成者线程将获得互斥锁,并获得了对临界区资源的独占处理权及cpu资源的优先使用权,然后开始执行自己的线程函数。生成者线程向临界区资源注入数据,如向队列中注入待解码的数据包,然后,通过pthread_cond_signal唤醒消费者线程(图中虚线所示),随即通过unlock_mutex释放互斥锁

在生成者线程释放互斥锁后,消费者线程已被唤醒,并重新获取互斥锁,再次检查临界区资源,如果满足条件,则执行自己的线程函数,然后释放互斥锁,等待下一次执行。

这里需要说明一下,在实际情况下,并不总是消费者线程优先获得互斥锁,这是由cpu调度决定的。

下面给出一些示例代码来描述这个过程,例程中临界区资源的处理非常简单,只是对一个整型数据做+1处理,在实际的场景中,无论线程对临界区资源的处理有多么复杂的操作,生产者与消费者线程同步的原理与例程中的模型不会有任何差异。这里我们抽象出一个关于生产者-消费者的线程模型,方便大家理解

1.2 线程编译验证
下面给出例程的编译脚步,编译过程非常简单,在源码及Makefile编译脚本目录执行[make]命令,即可完成例程编译,执行下面的操作即可开始例程的运行。 Makefile编译脚本
1test: main.c
2    gcc -o test -g3 main.c -l pthread
3
4clean:
5    rm test

代码编译运行

1make
2./test

代码运行结果

 1./test 
2consumer_before_lock--------------0
3consumer_lock---------------------0
4consumer_wait---------------------0
5producer_before_lock++++++++++++++++++++++++++++++++0
6producer_lock+++++++++++++++++++++++++++++++++++++++0
7producer_process_critical_resource++++++++++++++++++0
8producer_unlock+++++++++++++++++++++++++++++++++++++1
9consumer_revive-------------------1
10consumer_wait---------------------1
11producer_before_lock++++++++++++++++++++++++++++++++1
12producer_lock+++++++++++++++++++++++++++++++++++++++1
13producer_process_critical_resource++++++++++++++++++1
14producer_unlock+++++++++++++++++++++++++++++++++++++2
15consumer_revive-------------------2
16consumer_wait---------------------2
17producer_before_lock++++++++++++++++++++++++++++++++2
18producer_lock+++++++++++++++++++++++++++++++++++++++2
19producer_process_critical_resource++++++++++++++++++2
20producer_unlock+++++++++++++++++++++++++++++++++++++3
21consumer_revive-------------------3
22consumer_wait---------------------3
23producer_before_lock++++++++++++++++++++++++++++++++3
24producer_lock+++++++++++++++++++++++++++++++++++++++3
25producer_process_critical_resource++++++++++++++++++3
26producer_unlock+++++++++++++++++++++++++++++++++++++4
27consumer_revive-------------------4
28consumer_wait---------------------4
29producer_before_lock++++++++++++++++++++++++++++++++4
30producer_lock+++++++++++++++++++++++++++++++++++++++4
31producer_process_critical_resource++++++++++++++++++4
32producer_unlock+++++++++++++++++++++++++++++++++++++5
33consumer_revive-------------------5
34consumer_wait---------------------5
35producer_before_lock++++++++++++++++++++++++++++++++5
36producer_lock+++++++++++++++++++++++++++++++++++++++5
37producer_process_critical_resource++++++++++++++++++5
38producer_unlock+++++++++++++++++++++++++++++++++++++6
39consumer_revive-------------------6
40consumer_wait---------------------6
41producer_before_lock++++++++++++++++++++++++++++++++6
42producer_lock+++++++++++++++++++++++++++++++++++++++6
43producer_process_critical_resource++++++++++++++++++6
44producer_unlock+++++++++++++++++++++++++++++++++++++7
45consumer_revive-------------------7
46consumer_wait---------------------7
47producer_before_lock++++++++++++++++++++++++++++++++7
48producer_lock+++++++++++++++++++++++++++++++++++++++7
49producer_process_critical_resource++++++++++++++++++7
50producer_unlock+++++++++++++++++++++++++++++++++++++8
51consumer_revive-------------------8
52consumer_wait---------------------8
53producer_before_lock++++++++++++++++++++++++++++++++8
54producer_lock+++++++++++++++++++++++++++++++++++++++8
55producer_process_critical_resource++++++++++++++++++8
56producer_unlock+++++++++++++++++++++++++++++++++++++9
57consumer_revive-------------------9
58consumer_wait---------------------9
59producer_before_lock++++++++++++++++++++++++++++++++9
60producer_lock+++++++++++++++++++++++++++++++++++++++9
61producer_process_critical_resource++++++++++++++++++9
62producer_unlock+++++++++++++++++++++++++++++++++++++10
63consumer_revive-------------------10
64consumer_process_critical_resource------------------10
65consumer_unlock-----------------------0
66producer_before_lock++++++++++++++++++++++++++++++++0
67producer_lock+++++++++++++++++++++++++++++++++++++++0
68producer_process_critical_resource++++++++++++++++++0
69producer_unlock+++++++++++++++++++++++++++++++++++++1
70consumer_before_lock--------------0
71consumer_lock---------------------1
72consumer_wait---------------------1
73producer_before_lock++++++++++++++++++++++++++++++++1
74producer_lock+++++++++++++++++++++++++++++++++++++++1
75producer_process_critical_resource++++++++++++++++++1
76producer_unlock+++++++++++++++++++++++++++++++++++++2
77consumer_revive-------------------2
78consumer_wait---------------------2
79producer_before_lock++++++++++++++++++++++++++++++++2
80producer_lock+++++++++++++++++++++++++++++++++++++++2
81producer_process_critical_resource++++++++++++++++++2
82producer_unlock+++++++++++++++++++++++++++++++++++++3
83consumer_revive-------------------3
84consumer_wait---------------------3

可以看到,消费者线程不断的被唤醒,在临界区资源不满足执行条件的情况下,又阻塞到pthread_cond_wait并进入睡眠状态,重新等待生产者pthread_cond_signal的唤醒

更多线程同步的例程见[公众号:断点实验室]的其他文章

1.3 源码清单

大家也可以关注[公众号:断点实验室],留下邮箱地址,我会周期性的查看留言,给大家发送我修改过的源码

 1#include 
2#include 
3#include 
4#include 
5
6static int val=0;//临界区资源,critical resource
7
8pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥锁
9pthread_cond_t ready=PTHREAD_COND_INITIALIZER;//静态初始化条件变量
10
11//生产者临界区资源处理函数
12void producer_proc(int *cr){
13    printf("producer_process_critical_resource++++++++++++++++++%d\n\n",val);
14    *cr+=1;//处理临界区资源,做+1简单处理
15}
16
17//消费者临界区资源处理函数
18void consumer_proc(int *cr){
19    printf("consumer_process_critical_resource------------------%d\n\n",val);
20    *cr=0;//处理临界区资源,这里直接重置为0
21}
22
23//生产者线程函数
24void *producer_fun(void *p) {
25    while(1){
26        printf("producer_before_lock++++++++++++++++++++++++++++++++%d\n\n",val);
27        pthread_mutex_lock(&lock);//取得互斥锁
28        printf("producer_lock+++++++++++++++++++++++++++++++++++++++%d\n\n",val);
29
30        //处理临界区资源,做+1简单处理
31        producer_proc(&val);
32
33        pthread_cond_signal(&ready);//唤醒等待线程
34        printf("producer_unlock+++++++++++++++++++++++++++++++++++++%d\n\n",val);
35        pthread_mutex_unlock(&lock);//释放互斥锁
36        sleep(1);
37    }//end for while
38    return NULL;
39}
40
41//消费者线程函数
42void *consumer_fun(void *p) {
43    while(1){
44        printf("consumer_before_lock--------------%d\n\n",val);
45        pthread_mutex_lock(&lock);//取得互斥锁
46        printf("consumer_lock---------------------%d\n\n",val);
47        while(val<10) {//检查临界区资源是否满足执行条件
48            printf("consumer_wait---------------------%d\n\n",val);
49            //暂时释放互斥锁,休眠等待生产者线程发送就绪信号ready,该函数返回时互斥锁再次被锁住
50            pthread_cond_wait(&ready,&lock);
51            printf("consumer_revive-------------------%d\n\n",val);
52        }
53
54        //处理临界区资源,这里直接重置为0
55        consumer_proc(&val);
56
57        printf("consumer_unlock-----------------------%d\n\n",val);
58        pthread_mutex_unlock(&lock);
59        sleep(1);
60    }//end for while
61    return NULL;
62}
63
64int main(int argc,char *argv[]) {
65    pthread_t cid,pid;//线程id
66
67    //创建消费者线程
68    int ret=pthread_create(&cid,NULL,consumer_fun,NULL);
69    if(ret!=0) {//检查线程创建结果
70        printf("create consumer thread failed\n");
71        exit(1);
72    }
73
74    //创建生成者线程
75    ret=pthread_create(&pid,NULL,producer_fun,NULL);
76    if(ret!=0) {//检查线程创建结果
77        printf("create producer thread failed\n");
78        exit(1);
79    }
80
81    //等待线程结束
82    pthread_join(cid,NULL);
83    pthread_join(pid,NULL);
84
85    return 0;
86} 

———— / END / ————

// 如果觉得我的文章对您有用

// 请分享给更多人,不胜感激!

6ebf0c4c24be4a848ced1c20942efbe7.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值