C语言队列操作及其安全问题
在C语言中,队列是一种常用的数据结构,特别适用于嵌入式开发中的任务调度、缓冲区管理等场景。下面是一个简单的循环队列的模板代码,它使用数组来实现队列,并提供了基本的入队(enqueue)和出队(dequeue)操作。
示例代码如下:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#define QUEUE_MAX_SIZE 10 // 定义队列的最大容量
// 队列的结构体定义
typedef struct {
int data[QUEUE_MAX_SIZE]; // 存储队列元素的数组(如果需要动态扩展队列容量,可以考虑使用链表来实现队列)
int front; // 队列头部的索引
int rear; // 队列尾部的索引
} Queue;
// 初始化队列
void queueInit(Queue *q) {
q->front = q->rear = 0;
}
// 判断队列是否为空
bool isQueueEmpty(Queue *q) {
return q->front == q->rear;
}
// 判断队列是否已满
bool isQueueFull(Queue *q) {
return (q->rear + 1) % QUEUE_MAX_SIZE == q->front;
}
// 入队操作
bool enqueue(Queue *q, int value) {
if (isQueueFull(q)) {
return false; // 队列已满,无法入队
}
q->data[q->rear] = value;
q->rear = (q->rear + 1) % QUEUE_MAX_SIZE;
return true;
}
// 出队操作
bool dequeue(Queue *q, int *value) {
if (isQueueEmpty(q)) {
return false; // 队列为空,无法出队
}
*value = q->data[q->front];
q->front = (q->front + 1) % QUEUE_MAX_SIZE;
return true;
}
// 打印队列中的所有元素
void printQueue(Queue *q) {
int i = q->front;
for (int count = 0; count < (q->rear - q->front + QUEUE_MAX_SIZE) % QUEUE_MAX_SIZE; count++) {
printf("%d ", q->data[i]);
i = (i + 1) % QUEUE_MAX_SIZE;
}
printf("\n");
}
// 主函数,演示队列的使用
int main() {
Queue q;
queueInit(&q);
// 入队操作
enqueue(&q, 1);
enqueue(&q, 2);
enqueue(&q, 3);
// 打印队列
printf("Queue after enqueue: ");
printQueue(&q);
// 出队操作
int value;
dequeue(&q, &value);
printf("Dequeued value: %d\n", value);
// 再次打印队列
printf("Queue after dequeue: ");
printQueue(&q);
return 0;
}
这段代码定义了一个队列结构体,包括一个整型数组来存储队列元素,以及两个索引来分别表示队列的头部和尾部。 enqueue 函数用于在队列尾部添加元素, dequeue 函数用于从队列头部移除元素。 isQueueEmpty 和 isQueueFull 函数分别用于检查队列是否为空或满。
但是,这个队列实现是线程不安全的。在多线程环境中使用时,需要添加适当的同步机制来避免竞态条件。
考虑引入同步机制,帮助解决上述队列实现中的不安全问题:
1.互斥锁(Mutex): 互斥锁是一种基本的同步机制,用于保护共享资源不被多个线程同时访问。在队列操作中,可以在入队和出队操作前后使用互斥锁来确保每次只有一个线程可以修改队列。
#include <pthread.h>
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
bool enqueue(Queue *q, int value) {
pthread_mutex_lock(&queue_mutex);
if (isQueueFull(q)) {
pthread_mutex_unlock(&queue_mutex);
return false;
}
q->data[q->rear] = value;
q->rear = (q->rear + 1) % QUEUE_MAX_SIZE;
pthread_mutex_unlock(&queue_mutex);
return true;
}
bool dequeue(Queue *q, int *value) {
pthread_mutex_lock(&queue_mutex);
if (isQueueEmpty(q)) {
pthread_mutex_unlock(&queue_mutex);
return false;
}
*value = q->data[q->front];
q->front = (q->front + 1) % QUEUE_MAX_SIZE;
pthread_mutex_unlock(&queue_mutex);
return true;
}
2.条件变量(Condition Variable): 条件变量可以与互斥锁一起使用,以实现更高级的同步机制。它们允许线程在某些条件不满足时挂起,直到其他线程发出信号。
#include <pthread.h>
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queue_not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t queue_not_empty = PTHREAD_COND_INITIALIZER;
bool enqueue(Queue *q, int value) {
pthread_mutex_lock(&queue_mutex);
while (isQueueFull(q)) {
pthread_cond_wait(&queue_not_full, &queue_mutex); // 等待队列有空间
}
q->data[q->rear] = value;
q->rear = (q->rear + 1) % QUEUE_MAX_SIZE;
pthread_cond_signal(&queue_not_empty); // 通知可能有线程在等待出队
pthread_mutex_unlock(&queue_mutex);
return true;
}
bool dequeue(Queue *q, int *value) {
pthread_mutex_lock(&queue_mutex);
while (isQueueEmpty(q)) {
pthread_cond_wait(&queue_not_empty, &queue_mutex); // 等待队列中有元素
}
*value = q->data[q->front];
q->front = (q->front + 1) % QUEUE_MAX_SIZE;
pthread_cond_signal(&queue_not_full); // 通知可能有线程在等待入队
pthread_mutex_unlock(&queue_mutex);
return true;
}
3.读写锁(Read-Write Lock): 如果队列的读操作远多于写操作,使用读写锁可以提高性能。读写锁允许多个读操作同时进行,但写操作会独占锁。
#include <pthread.h>
pthread_rwlock_t queue_rwlock = PTHREAD_RWLOCK_INITIALIZER;
bool enqueue(Queue *q, int value) {
pthread_rwlock_wrlock(&queue_rwlock);
if (isQueueFull(q)) {
pthread_rwlock_unlock(&queue_rwlock);
return false;
}
// ... 入队操作
pthread_rwlock_unlock(&queue_rwlock);
return true;
}
bool dequeue(Queue *q, int *value) {
pthread_rwlock_wrlock(&queue_rwlock);
if (isQueueEmpty(q)) {
pthread_rwlock_unlock(&queue_rwlock);
return false;
}
// ... 出队操作
pthread_rwlock_unlock(&queue_rwlock);
return true;
}
void printQueue(Queue *q) {
pthread_rwlock_rdlock(&queue_rwlock);
// ... 打印队列操作
pthread_rwlock_unlock(&queue_rwlock);
}
4.原子操作: 某些编译器或硬件平台提供了原子操作,可以直接在不使用锁的情况下保证操作的原子性。这可以减少锁的开销,但通常只适用于简单的操作。
最后,需要注意,使用这些同步机制时,需要确保正确地初始化和销毁它们,并且在适当的时候获取和释放锁。同时,还需要考虑死锁的可能性,并采取相应的预防措施。
相关文章:
C语言队列操作及其安全问题
在C语言中,队列是一种常用的数据结构,特别适用于嵌入式开发中的任务调度、缓冲区管理等场景。下面是一个简单的循环队列的模板代码,它使用数组来实现队列,并提供了基本的入队(enqueue)和出队(de…...
next.js v14 升级全步骤|迁移 pages Router 到 App Router
【概括】本文升级整体按照官网文档指引进行,在迁移 pages Router 前先看了官网的实操视频。 【注意】文章内对 .babel.ts、next.config.js 进行了多次更改,最终配置可见 报错3: Server Error ReferenceError: React is not defined 一、升级 Next.js 版…...
如何在Ubuntu上安装WordPress
如何在Ubuntu上安装WordPress 执行系统更新 apt update && apt upgrade第一步 安装 Apache apt install apache2确认 Apache 安装是否成功. systemctl status apache2安装成功后 打开浏览器输入 http://server-ip-address 第二步 安装 MySQL apt install mariad…...
处理导入Excel文件过大导致Zip bomb detected的问题
处理导入Excel文件过大导致Zip bomb detected的问题 处理导入Excel文件过大导致Zip bomb detected的问题解决方案完整示例代码处理内存溢出问题优化处理大文件的策略 处理导入Excel文件过大导致Zip bomb detected的问题 在Java应用中导入Excel文件时,可能会遇到文件…...
【FFmpeg】AVIOContext结构体
【FFmpeg】AVIOContext结构体 1.AVIOContext结构体的定义 参考: FFMPEG结构体分析:AVIOContext 示例工程: 【FFmpeg】调用ffmpeg库实现264软编 【FFmpeg】调用ffmpeg库实现264软解 【FFmpeg】调用ffmpeg库进行RTMP推流和拉流 【FFmpeg】调用…...
Python控制结构
文章目录 控制结构1. 条件语句1.1 if语句1.2 elif语句1.3 else 语句 2. 循环语句2.1 for循环2.2 while循环 控制循环的语句3.1 break语句3.2 continue语句3.3 else语句与循环配合 控制结构 Python中的控制结构是指管理代码执行流程的语句和机制,包括条件语句、循环…...
OpenCV--图形轮廓
图形轮廓 图像轮廓查找轮廓绘制轮廓计算轮廓的面积和周长多边形逼近与凸包外接矩形 图像轮廓 import cv2 import numpy as np""" 图形轮廓--具有相同颜色或灰度的连续点的曲线 用于图形分析和物体的识别和检测 注意:为了检测的准确性,必…...
MYSQL通过EXPLAIN关键字来分析SQL查询的执行计划,判断是否命中了索引
在MySQL中,你可以通过EXPLAIN关键字来分析SQL查询的执行计划,从而判断是否命中了索引。 准备查询语句: 首先,你需要一个带有WHERE子句的SELECT查询,因为WHERE子句中的条件通常与索引相关联。例如: SELECT …...
clean code-代码整洁之道 阅读笔记(第十二章)
第十二章 系统 12.1 通过选进设计达到整洁目的 Kent Beck关于简单设计的四条规则,对于创建具有良好设计的软件有着莫大的帮助。 据Kent所述,只要遵循以下规则,设计就能变得"简单":运行所有测试;不可重复&…...
FFmpeg YUV编码为H264
使用FFmpeg库把YUV420P文件编码为H264文件,FFmpeg版本为4.4.2-0。 需要yuv测试文件的,可以从我上传的MP4文件中用ffmpeg提取,命令如下: ffmpeg -i <input.mp4> -pix_fmt yuv420p <output.yuv> 代码如下:…...
【C语言】顺序表(上卷)
什么是数据结构? 数据结构是由“数据”和“结构”两词组合而来的。 数据需要管理。数据结构就是计算机存储、组织数据的方式。比如一个班级就是一个结构,管理的就是班级里的学生。如果我们要找三年2班的同学李华,就可以直接去三年2班找而不…...
Luma AI如何注册:文生视频领域的新星
文章目录 Luma AI如何注册:文生视频领域的新星一、Luma 注册方式二、Luma 的效果三、Luma 的优势四、Luma 的功能总结 Luma AI如何注册:文生视频领域的新星 近年来,Luma AI 凭借其在文生视频领域的创新技术,逐渐成为行业的新星。…...
一站式实时数仓Hologres整体能力介绍
讲师:阿里云Hologres PD丁烨 一、产品定位 随着技术的进步,大数据正从规模化转向实时化处理。用户对传统的T1分析已不满足,期望获得更高时效性的计算和分析能力。例如实时大屏,城市大脑的交通监控、风控和实时的个性化推荐&…...
如何在 Windows 上安装 Docker Desktop
如何在 Windows 上安装 Docker Desktop Docker 是一个开放平台,用于开发、部署和运行应用程序。Docker Desktop 是 Docker 在 Windows 和 macOS 上的官方客户端,它使得开发者能够轻松地在本地环境中构建、运行和共享容器化应用程序。本文将详细介绍如何…...
WPF由文本框输入的内容动态渲染下拉框
在做项目过程中,需要扫码枪扫描快递单号或者手动输入快递单号时,自动检索该单号是哪个快递公司的,下拉框中自动带出该单号的快递公司。当输入的快递单号不存在时,将数据库中所有快递公司都带出 效果: 通过输入的快递单…...
RPCMon:一款基于ETW的RPC监控工具
关于RPCMon RPCMon是一款基于事件跟踪的WindowsRPC监控工具,该工具是一款GUI工具,可以帮助广大研究人员通过ETW(Event Tracing for Windows)扫描RPC通信。 RPCMon能够为广大研究人员提供进程之间RPC通信的高级视图,该…...
【odoo】常用的字符转义:“>“,“<“,““,“/“等
概要 字符转义是指在编写代码或处理文本数据时,将特殊字符转换为另一种形式,以便在特定的上下文中正确解析和处理这些字符。 内容 特殊字符描述XML转义表示法&和符号&<小于符号<>大于符号>"双引号"单引号&ap…...
李宏毅深度学习项目——HW1个人笔记
视频链接 PDF链接 googleColab链接 GoogleColab是一个免费的jupyter notebook,可以用上面的gpu资源进行训练 题目 通过前两天的数据,预测第三天某个人感染新冠的概率 范例 导包 # Numerical Operations import math import numpy as np# Reading/Wr…...
3D Gaussian Splatting Windows安装
0.安装C++ 编译器 https://aka.ms/vs/17/release/vs_buildtools.exe 1.下载源码 git clone https://github.com/graphdeco-inria/gaussian-splatting --recursive 2.安装cuda NVIDIA GPU Computing Toolkit CUDA Toolkit Archive | NVIDIA Developer 3.安装COLMAP...
人脸识别——可解释的人脸识别(XFR)人脸识别模型是根据什么来识别个人的
可解释性人脸识别(XFR)? 人脸识别有一个任务叫1:N(识别)。这个任务将一个人的照片与N张注册照片进行比较,找出相似度最高的人。 这项任务用于刑事调查和出入境点。在犯罪调查中,任务从监控摄像…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
