尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

面试手撕---线程安全双缓冲区---C语言

面试手撕---线程安全双缓冲区---C语言 线程安全的乒乓缓冲区单写者多读者伪代码结构体 PingPong{缓冲区 buffer[2]原子变量 version// 从1开始大小 data_size}/******************************************/函数Init(handle_ptr,data_size):分配 PingPong 结构体内存 分配 buffer[0]内存(data_size 字节)分配 buffer[1]内存(data_size 字节)将两个缓冲区清零 设置 data_size 设置 version1// 1表示buffer[0]可读buffer[1]可写*handle_ptr指向结构体 返回 成功 函数Destroy(ppb):释放 buffer[0]释放 buffer[1]设置 data_size0设置 version0释放 ppb 结构体/******************************************/函数Write(ppb,data):// 获取当前版本号current_version原子加载(ppb-version)// 计算写入缓冲区索引偶数写buffer[0]奇数写buffer[1]write_idxcurrent_version%2// 将数据复制到写缓冲区拷贝 data 到 ppb-buffer[write_idx](data_size 字节)// 版本号1使刚写入的缓冲区变为可读原子增加 ppb-version(加1)//版本号一直累加直达溢出依然安全---只区分奇偶返回 成功 函数Read(ppb,data):循环:// 读取版本号version_before原子加载(ppb-version)// 计算读缓冲区索引前一个版本read_idx(version_before-1)%2// 复制数据拷贝 ppb-buffer[read_idx]到data(data_size 字节)// 再次读取版本号检查是否发生变化version_after原子加载(ppb-version)//如果版本号未变数据一致读取成功如果 version_beforeversion_after:返回 成功// 版本号已变说明写操作在读取过程中发生重试让出CPU(thrd_yield)继续循环头文件.h#ifndef_PING_PONG_BUFC_H_#define_PING_PONG_BUFC_H_#includestdint.h#includestdbool.h#includestdatomic.h#includethreads.h#ifdef__cplusplusexternC{#endiftypedefstructPING_PONG_TPING_PONG;/** * brief 初始化乒乓缓冲区 * * param handle 指向乒乓缓冲区结构体的指针 * param data_size 单个数据元素的大小字节数 * return int32_t 成功返回0失败返回-1 */int32_tpingpong_init(PING_PONG**handle,size_tdata_size);/** * brief 销毁乒乓缓冲区并释放内存 * * param ppb 指向乒乓缓冲区结构体的指针 */voidpingpong_destroy(PING_PONG*ppb);/** * brief 向缓冲区写入数据仅限单写者使用 * * param ppb 指向乒乓缓冲区结构体的指针 * param data 指向要写入数据的指针 * return int32_t 成功返回0失败返回-1 */int32_tpingpong_write(PING_PONG*ppb,constvoid*data);/** * brief 向缓冲区读取数据 * * param ppb 指向乒乓缓冲区结构体的指针 * param data 指向要读取数据的指针 * return int32_t 成功返回0失败返回-1 */int32_tpingpong_read(PING_PONG*ppb,void*data);/** * brief 检查缓冲区是否为空尚未执行过写入操作 * * param ppb 指向乒乓缓冲区结构体的指针 * return bool 为空返回true否则返回false */boolpingpong_empty(constPING_PONG*ppb);#ifdef__cplusplus}#endif#endif// _PING_PONG_BUFC_H_源文件.c#includestdlib.h#includestring.h#includestdatomic.h#includethreads.h#includepingpong_bufc.h/** * brief 乒乓缓冲区结构体隐藏实现 * * 提供双缓冲机制一个缓冲区用于写入另一个用于读取两者角色可以互换。 * 版本号计数器确保读者看到一致的数据。 */structPING_PONG_T{void*buffers[2];// 双缓冲区指针atomic_ullong version;// 版本号用于同步size_tdata_size;// 单个数据元素的大小字节数};int32_tpingpong_init(PING_PONG**handle_ptr,size_tdata_size){if(data_size0||handle_ptrNULL){return-1;}PING_PONG*ppb(PING_PONG*)malloc(sizeof(PING_PONG));if(ppbNULL){return-1;}// 分配两个缓冲区内存ppb-buffers[0]malloc(data_size);ppb-buffers[1]malloc(data_size);if(ppb-buffers[0]NULL||ppb-buffers[1]NULL){if(ppb-buffers[0]!NULL)free(ppb-buffers[0]);if(ppb-buffers[1]!NULL)free(ppb-buffers[1]);free(ppb);return-1;}// 正确初始化缓冲区内存memset(ppb-buffers[0],0,data_size);memset(ppb-buffers[1],0,data_size);ppb-data_sizedata_size;// 初始化版本号为1确保第一个可读的buffer索引为0atomic_init(ppb-version,1);*handle_ptrppb;// 修改调用者的指针return0;}voidpingpong_destroy(PING_PONG*ppb){if(ppbNULL){return;}free(ppb-buffers[0]);free(ppb-buffers[1]);ppb-buffers[0]NULL;ppb-buffers[1]NULL;ppb-data_size0;atomic_store(ppb-version,0);free(ppb);}int32_tpingpong_write(PING_PONG*ppb,constvoid*data){if(ppbNULL||dataNULL){return-1;}// 获取当前版本号计算写入的缓冲区索引unsignedlonglongcurrent_versionatomic_load_explicit(ppb-version,memory_order_acquire);size_twrite_idx(size_t)(current_version%2);// 写入数据memcpy(ppb-buffers[write_idx],data,ppb-data_size);// 增加版本号发布写入完成// 使用memory_order_release确保写入对其他线程可见atomic_fetch_add_explicit(ppb-version,1,memory_order_release);return0;}int32_tpingpong_read(PING_PONG*ppb,void*data){if(ppbNULL||dataNULL){return-1;}unsignedlonglongversion_before,version_after;do{// 读取当前版本号version_beforeatomic_load_explicit(ppb-version,memory_order_acquire);// 检查是否有数据可读if(version_before1){return-1;// 无数据可读}// 计算读缓冲区索引并复制数据size_tread_idx(size_t)((version_before-1)%2);memcpy(data,ppb-buffers[read_idx],ppb-data_size);// 再次读取版本号检查在复制过程中是否有新的写入version_afteratomic_load_explicit(ppb-version,memory_order_acquire);// 如果版本号未变说明读取的数据是一致的if(version_beforeversion_after){return0;// 读取成功}// 版本号已变说明读取过程中有新的写入重试// 让出CPU避免忙等待消耗太多CPU资源thrd_yield();}while(1);return0;}// size_t pingpong_get_read_index(const PING_PONG* ppb)// {// if (ppb NULL)// {// return 0;// }// unsigned long long version atomic_load_explicit(ppb-version, memory_order_acquire);// // 可读的缓冲区是版本号减1然后取模2// return (size_t)((version - 1) % 2);// }// size_t pingpong_get_write_index(const PING_PONG* ppb)// {// if (ppb NULL)// {// return 0;// }// unsigned long long version atomic_load_explicit(ppb-version, memory_order_acquire);// // 当前正在写的缓冲区是版本号取模2// return (size_t)(version % 2);// }boolpingpong_empty(constPING_PONG*ppb){if(ppbNULL){returntrue;}returnatomic_load_explicit(ppb-version,memory_order_acquire)1;}
返回列表