c语言多线程缓冲队列无锁设计思路

公司里开发的一个项目需要在server端添加多线程缓冲队列,来存取数据,我也是初出茅庐没有太多经验,在网上搜集了大量资料后,终于有了一套自己的设计思路,并解决了项目里的问题,因为当时搜集资料时,发现网上这个的具体文章不是太多或者要么太复杂,要么太简陋,对于新手很难能看懂,所有我就打算将我的设计思路发出来,希望能帮助和我一样的同学朋友们,如有不足请指导!谢谢

项目需求:

两个线程,线程1接收客户端数据并有序的存入缓冲队列;线程2从缓冲队列有序的取出数据并解析插入数据库;

解决方法:

1:新建一个结构体buff_quere,内有一个枚举类型buffstatus缓冲区状态,为空 已读 已写,和一个缓存数据的数组类型buff_up_quere,接收的数据存在这里;(全局)

  

1 /* 缓冲区结构体 */
2 struct buff_quere{
3     enum buffstatus{empty,wirte,reads}bufstatus;  //为空,已写,已读
4     uint8_t buff_up_quere[RX_BUFF_SIZE];            //缓冲区
5 };

2:创建结构体数组为缓冲区,需要几个就建几个;(全局)

1 static struct buff_quere buff_data1[BUFFER_QUEUE_LEN];//缓冲区1
2 static int count_len1 = 0;              //缓冲区1计数器
3 static struct buff_quere buff_data2[BUFFER_QUEUE_LEN];//缓冲区2
4 static int count_len2 = 0;              //缓冲区2计数器

3:线程1接收并储存数据到缓冲区;

 1 int buff_nb = 1;//默认从第一个缓冲区开始储存
 2 int nb_next = 0;//下一个缓冲区
 3 int len=0;
 4 while(true)
 5 {
 6 //缓冲队列
 7 nb_next = buff_nb+1;
 8 if(nb_next == 2){
 9    nb_next = 1;
10 }
11 switch(buff_nb){
12             case 1:
13                 printf("进入缓冲区%d进行写入缓冲区index=%d\n",buff_nb,len);
14                 buff_data1[len].bufstatus = wirte;
15                 memcpy(&buff_data1[len].buff_up_quere, &buff_up, sizeof(buff_up));
16                 len++;
17                 if (len == BUFFER_QUEUE_LEN)
18                 {
19                     printf("缓冲区%d已写入完毕",buff_nb);
20                     while(count_len2 != 0){
21                         printf("警告:缓冲区%d还未清空,停止缓存数据\n",nb_next);
22                         sleep(1);
23                     }
24                     printf(",转到缓冲区%d进行缓存\n",nb_next);
25                     len = 0;
26                     buff_nb = nb_next;
27                 }
28             break;
29             case 2:
30                 printf("进入缓冲区%d进行写入缓冲区index=%d\n",buff_nb,len);
31                 buff_data2[len].bufstatus = wirte;
32                 memcpy(&buff_data2[len].buff_up_quere, &buff_up, sizeof(buff_up));
33                 len++;
34                 if (len == BUFFER_QUEUE_LEN)
35                 {
36                     printf("缓冲区%d已写入完毕",buff_nb);
37                     while(count_len1 != 0){
38                         printf("警告:缓冲区%d还未清空,停止缓存数据\n",nb_next);
39                         sleep(1);
40                     }
41                     printf(",转到缓冲区%d进行缓存\n",nb_next);
42                     len = 0;
43                     buff_nb = nb_next;
44                 }
45             break;
46             default:
47             continue;
48             break;
49 }    

4:线程2从缓冲区获取数据并操作数据;

 1 int buff_nb = 1;//获取缓冲区编号,默认为1
 2 int nb_next = 0;//下一个缓冲区
 3 int len=0;
 4 while (true)
 5 {
 6     //缓冲队列
 7         nb_next = buff_nb+1;
 8         if(nb_next == 11){
 9             nb_next = 1;
10         }
11         switch(buff_nb){
12             case 1:
13                 if(buff_data1[len].bufstatus == wirte){
14                     printf("进入缓冲区%d进行读取缓冲区数据index=%d\n",buff_nb,len);
15                     buff_data1[len].bufstatus = reads;
16                     memcpy(&buff_up, &buff_data1[len].buff_up_quere, sizeof(buff_data1[len].buff_up_quere));
17                     len++;
18                     if (len == BUFFER_QUEUE_LEN)
19                     {
20                         printf("缓冲区%d已读取完毕index=%d,清空此缓冲区\n",buff_nb,len);
21                         len = 0;
22                         buff_nb = nb_next;
23                         memset(buff_data1, 0, sizeof(buff_data1));
24                     }
25                 }else{
26                     printf("缓冲区%d为空或已读,请等待...\n",buff_nb);
27                     sleep(1);
28                     continue;
29                 }
30                 count_len1 = len;
31             break;
32             case 2:
33                 if(buff_data2[len].bufstatus == wirte){
34                     printf("进入缓冲区%d进行读取缓冲区数据index=%d\n",buff_nb,len);
35                     buff_data2[len].bufstatus = reads;
36                     memcpy(&buff_up, &buff_data2[len].buff_up_quere, sizeof(buff_data2[len].buff_up_quere));
37                     len++;
38                     if (len == BUFFER_QUEUE_LEN)
39                     {
40                         printf("缓冲区%d已读取完毕index=%d,清空此缓冲区\n",buff_nb,len);
41                         len = 0;
42                         buff_nb = nb_next;
43                         memset(buff_data2, 0, sizeof(buff_data2));
44                     }
45                 }else{
46                     printf("缓冲区%d为空或已读,请等待...\n",buff_nb);
47                     sleep(1);
48                     continue;
49                 }
50                 count_len2 = len;
51             break;
52             default:
53             continue;
54             break;
55 }

总结思路:当线程1将数据储存到缓冲区1的时候先将缓冲区每一个储存的元素赋值一个写入的状态,然后再写入;当缓冲区1写满后通过count_len2是否为0判断缓冲区2是否为空,为空就切换缓冲区2继续写入,否则进行等待;

当线程2从缓冲队列获取数据的时候,先判断队列里每一个元素的状态是否为写入,是则获取,不是就等待,当获取完此缓冲区就清空掉,并将count_len2=0,告诉线程1我清空了,你可以写了!

时间: 2024-08-11 15:39:56

c语言多线程缓冲队列无锁设计思路的相关文章

谈谈存储软件的无锁设计

面向磁盘设计的存储软件不需要考虑竞争锁带来的性能影响.磁盘存储软件的性能瓶颈点在于磁盘,磁盘抖动会引入极大的性能损耗.因此,传统存储软件的设计不会特别在意处理器的使用效率.曾经对一个存储虚拟化软件进行性能调优,在锁竞争方面做了大量优化,最后也没有达到性能提升的效果,原因就在于存储虚拟化的性能瓶颈点在于磁盘,而不在于处理器的使用效率.正因为如此,在面向磁盘设计的软件中,很多都采用单线程.单队列处理的方式,一定程度上还可以避免由于并发所引入的磁盘抖动问题. 在面向NVMe SSD设计的存储软件中,这

多线程编程之无锁队列

关于无锁队列的概念与实现,可以参考博文<无锁队列的实现>,主要涉及到的知识点包括CAS原子操作.无锁队列的链表实现.无锁队列的数组实现以及ABA问题. 下面借鉴了<多线程的那点儿事(之无锁队列)>的代码,说明两个线程(一个添加一个读取数据)之间的无锁队列,可以不借助线程互斥方法就能够达到并行效果.代码如下: #define MAX_NUMBER 1000L #define STATUS int #define OK 0 #define FALSE -1 typedef struct

一个高性能无锁哈希表的设计和实现

无锁哈希表(Lock-Free Hash Table)是多线程编程中的理想数据结构,但是实现以及使用都需要一定的技巧.作者对此做了一个巧妙的设计实现,在现代X86平台上能取得千万次每秒的并发查找/增加/删除操作. 通过考察各种基于CAS原子操作的无锁数据结构实现,目前公认可实现无锁安全的数据结构是数组和单向队列.其他实现都一定程度上受到ABA问题的威胁.数组的实现相对于单向队列要简单,所以无锁hash table理想的选择是数组,对于冲突不拉链.但是如何解决hash冲突呢?基本思想依然是开放寻址

双缓冲队列来减少锁的竞争

双缓冲队列来减少锁的竞争 在日常的开发中,日志的记录是必不可少的.但是我们也清楚对同一个文本进行写日志只能单线程的去写,那么我们也经常会使用简单lock锁来保证只有一个线程来写入日志信息.但是在多线程的去写日志信息的时候,由于记录日志信息是需要进行I/O交互的,导致我们占用锁的时间会加长,从而导致大量线程的阻塞与等待. 这种场景下我们就会去思考,我们该怎么做才能保证当有多个线程来写日志的时候我们能够在不利用锁的情况下让他们依次排队去写呢?这个时候我们就可以考虑下使用双缓冲队列来完成. 所谓双缓冲

利用双缓冲队列来减少锁的竞争

在日常的开发中,日志的记录是必不可少的.但是我们也清楚对同一个文本进行写日志只能单线程的去写,那么我们也经常会使用简单lock锁来保证只有一个线程来写入日志信息.但是在多线程的去写日志信息的时候,由于记录日志信息是需要进行I/O交互的,导致我们占用锁的时间会加长,从而导致大量线程的阻塞与等待. 这种场景下我们就会去思考,我们该怎么做才能保证当有多个线程来写日志的时候我们能够在不利用锁的情况下让他们依次排队去写呢?这个时候我们就可以考虑下使用双缓冲队列来完成. 所谓双缓冲队列就是有两个队列,一个是

C++ boost库无锁队列多线程并行测试与编译方法

阅读了网络中关于Boost库无锁队列的源代码,但却缺少编译方法.经过测试,确定了ubuntu 14.04中编译boost库的方法,特做记录. 无锁(free-lock)是实现高性能多线程并发编程的重要技术. 作为C++11 STL参考实现的boost库,不仅支持11标准,而且做了许多扩展,掌握其使用方法,对于提高代码质量,尤其重要. 以其多线程并行无锁队列为例,结合代码和说明,演示了无锁boost库的使用和编译方法. 代码及说明如下: //source: boost_queue.cpp //目的

多线程操作C++ STL vector出现概率coredump问题及尽量避免锁的双缓冲队列

多线程操作全局变量,必须考虑同步问题,否则可能出现数据不一致, 甚至触发coredump. 前段时间, 遇到一个多线程操作了全局的vector的问题,  程序崩了.场景是这样的:某全局配置参数保存在一个vector中,需要定时更新(更新线程), 另外的工作线程去读取配置. 这种场景是非常普遍的. 在该场景中,程序没有枷锁,概率coredump, 实际情况是,服务跑了一段时间后,必然coredump.   很显然, 更新线程执行clear,然后在push_back操作时, 会导致工作线程的vect

Go语言无锁队列组件的实现 (chan/interface/select)

1. 背景 go代码中要实现异步很简单,go funcName(). 但是进程需要控制协程数量在合理范围内,对应大批量任务可以使用"协程池 + 无锁队列"实现. 2. golang无锁队列实现思路 Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication).无锁队列使用带buff的chan存储数据. interface{} (类似c++的void*, java的Object)可以与任意类型互转.无锁队列使

linux内核无锁缓冲队列kfifo原理

Linux kernel里面从来就不缺少简洁,优雅和高效的代码 比如,通过限定写入的数据不能溢出和内存屏障实现在单线程写单线程读的情况下不使用锁.因为锁是使用在共享资源可能存在冲突的情况下.还用设置buffer缓冲区的大小为2的幂次方,以简化求模运算,这样求模运算就演变为 (fifo->in & (fifo->size - 1)).通过使用unsigned int为kfifo的下标,可以不用考虑每次下标超过size时对下表进行取模运算赋值,这里使用到了无符号整数的溢出回零的特性.由于指