服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言通过案例讲解并发编程模型

C语言通过案例讲解并发编程模型

2022-11-10 14:40炸毛疯兔 C/C++

所谓并发编程是指在一台处理器上“同时”处理多个任务。并发是在同一实体上的多个事件。多个事件在同一时间间隔发生,下面我们根据样例来理解

下面代码、思路等来源于b站郭郭 和CSAPP样例,同时希望大家好好读一下CSAPP的内容,真的讲的很好

1、按照指定的顺序输出

我们执行两个线程:foo1foo2

foo1:打印step1, step3

foo2:打印step2

请用并发使得按照1 2 3 的顺序输出

答:首先两个线程执行顺序不可预判,我们必须保证打印step2之前step1就打印好了,因此需要阻塞一下step2,实现的方式是初始化sem为0,只有打印完step1后(然后进行解锁,V操作)step2才能执行

同理,只有打印完step2后才解开阻塞step3的锁,具体看代码实现就明白了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "csapp.c"
 
 
sem_t step1_done, step2_done;
 
void*  foo1() {
    printf("test1 is done\n");
    V(&step1_done);                  //step1执行完毕了,那么foo2的阻塞就会被解开
    P(&step2_done);                  //测试是否step2执行完毕,
    printf("test3 is done\n");
    return NULL;
}
 
void* foo2() {
    P(&step1_done);
    printf("test2 is done\n");
    V(&step2_done);                  //step2执行完毕,解开打印step的锁
    return NULL;
}
 
int main()
{
    pthread_t tid1, tid2;
    Sem_init(&step1_done, 0, 0);            //第二个参数为0:在线程之间进行, 第三个参数初始化都为零
    Sem_init(&step2_done, 0, 0);
 
 
    Pthread_create(&tid1, NULL, foo1, NULL);
    Pthread_create(&tid2, NULL, foo2, NULL);
 
 
    //保证线程执行完毕之后主线程才退出,否则线程都执行不了了
    Pthread_join(tid1, NULL);
    Pthread_join(tid2, NULL);
 
 
    exit(0);
 
}

2、生产者消费者模型

主要的就是在生产和消费函数中对于信号量的处理

错误实例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void sbuf_insert(subf_t* sp, int item) {
    sem_wait(&sp->mutex);
    sem_wait(&sp->slots);
 
    //将项目放进buf中
    sp->buf[(++sp->rear) % (sp->n)] = item;
 
    sem_post(&sp->items);
    sem_post(&sp->mutex);
 
}
 
 
void sbuf_remove(sbuf_t* sp) {
  sem_wait(&sp->mutex);
  sem_wait(&sp->items);
  
  
  //do works
  
  sem_post(&sp->slots);
  sem_post(&sp->mutex);
}

如果我们在处理的时候先拿到 互斥锁,可能就会引起死锁

假设现在buf是满的,生产者拿到了互斥锁,但是自己因为没有空闲被 block…

此时消费者同样因为拿不到互斥锁而被 block…

其他的生产者同样也是没有 互斥锁被block…

解决方法:

比较简单,调换一下顺序就好了。相当于我们生产者、消费者在进行的时候 明确我到底要操控哪个格子 然后再拿mutex锁

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
 
typedef struct sbuf{
    int *buf;               /*堆上开辟的内存,用于存储*/
    int n;                  /*cap of the buf*/
    int front;              //第一个item
    int rear;               //最后一个item
 
    sem_t mutex;            //获取临界区的锁
    sem_t slots;            //空槽数目
    sem_t items;            //已经生产了的数目
}subf_t;
 
void sbuf_init(subf_t* sp, int n) {
    sp->n     = n;
    sp->buf   = static_cast<int *>(calloc(n, sizeof(int)));
    sp->front = 0;
    sp->rear  = 0;
 
    sem_init(&sp->mutex, 0, 1);
    sem_init(&sp->slots, 0, n);
    sem_init(&sp->items, 0, 0);
}
 
void sbuf_deinit(subf_t*sp) {
    free(sp->buf);
}
 
 
void sbuf_insert(subf_t* sp, int item) {
    //首先应该对信号量slots判断,你生产者看中
    sem_wait(&sp->slots);
    sem_wait(&sp->mutex);
 
    //将项目放进buf中
    sp->buf[(++sp->rear) % (sp->n)] = item;
 
 
    //CSAPP中提到,解锁的顺序一般是和加锁的顺序是相反的
    sem_post(&sp->mutex);
    sem_post(&sp->items);
}
 
int  sbuf_remove(subf_t* sp) {
    int item;
    sem_wait(&sp->items);       //我看上哪个格子的产品了
    sem_wait(&sp->mutex);
 
    item = sp->buf[(++sp->front) % (sp->n)];
 
    sem_post(&sp->mutex);
    sem_post(&sp->slots);
    return item;
}

3、读写锁

第一类读者、写者问题(读者优先)

  • 不会让读者进行等待的,除非现在的权限是写者的
  • 也就是说读者不会因为有一个写者在等待

实现:

信号量:w维护着对于critical section的访问, mutex维护这对于共享变量readcnt(当前在临界区的读者的数量)的访问

每当写者进入了临界区,就对w进行加锁锁,离开就解锁。保证了任意时刻临界区最多只能有一个写者

只有第一个读者进入的时候对W加锁,最后一个才释放,那么只要还有一个读者在,其他任意的读者就能够无障碍的进入,同样会导致 写者饥饿

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int readcnt = 0;
sem_t ,mutex = 1, w = 1;
 
void reader() {
  while (1) {
    P(&mutex);
    readcnt++;
    if (readcnt == 1)   //第一个进入的读者
      P(&w);                        //上锁,写者不能写了
    V(&mutex);                  //解开对于readcnt的保护锁
    
    /*
            临界区的工作
    
    */
    
    P(&mutex);
    readcnt--;
    if (readcnt == 0)
      V(&w);                                //最后一个读者了, 解开阻塞写者的锁
    V(&mutex);                          //解开对readcnt的保护锁
  }
}
 
void writer() {
  while (1) {
    P(&w);
    
    /*
        临界区工作
    
    */
    V(&w);
  }
}

到此这篇关于C语言通过案例讲解并发编程模型的文章就介绍到这了,更多相关C语言 并发编程内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq_52245648/article/details/124176984

延伸 · 阅读

精彩推荐
  • C/C++C语言实现计算器的两种方法

    C语言实现计算器的两种方法

    这篇文章主要为大家详细介绍了C语言实现计算器的两种方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    猪饲夫妇、6902022-09-08
  • C/C++基于C语言代码实现扫雷游戏

    基于C语言代码实现扫雷游戏

    这篇文章主要为大家详细介绍了基于C语言代码实现扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    楠c12332021-10-06
  • C/C++解析C++编程中的bad_cast异常

    解析C++编程中的bad_cast异常

    这篇文章主要介绍了C++编程中的bad_cast异常,bad_cast异常通常出现于表达式中类型转换错误时等一些场景,需要的朋友可以参考下...

    C++教程网6502021-03-22
  • C/C++深入理解c语言数组

    深入理解c语言数组

    这篇文章主要介绍了c语言数组,有需要的朋友可以参考一下...

    C语言教程网8082021-01-11
  • C/C++如何用C写一个web服务器之基础功能

    如何用C写一个web服务器之基础功能

    C语言是一门很基础的语言,程序员们对它推崇备至,本文将带着大家来看一下,如何用C写一个web服务器。...

    枕边书9852021-11-09
  • C/C++C/C++中的typedef和#define详解

    C/C++中的typedef和#define详解

    这篇文章主要介绍了C/C++中的typedef和#define详解的相关资料,需要的朋友可以参考下...

    C语言教程网10592021-05-03
  • C/C++C语言实现字符串转浮点函数的示例

    C语言实现字符串转浮点函数的示例

    字符串不仅可以转换为整数,也可以转换为浮点数,本文主要介绍了C语言实现字符串转浮点函数的示例,具有一定的参考价值,感兴趣的可以了解一下...

    嵌入式@hxydj6632022-09-26
  • C/C++C++空类详解

    C++空类详解

    以下是对C++中的空类进行了详细的介绍,需要的朋友可以过来参考下...

    C++教程网9462020-12-26