c-线程创建,同步互斥,互斥锁;

文章目录

  • 案例描述1
    • 代码实现
    • 代码解释
  • 案例背景2
    • 代码实现
    • 代码解析
    • 关键概念总结
    • 扩展练习

案例描述1

我们将模拟一个简单的售票系统,其中有两个售票窗口同时出售100张票。为了确保不会卖出超过100张票,并且不会出现卖票时的竞态条件(race condition),我们将使用互斥锁来保护共享资源——剩余票数。

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>#define NUM_TICKETS 100
#define NUM_THREADS 2int tickets = NUM_TICKETS;
pthread_mutex_t mutex;void* sell_tickets(void* arg) {while (1) {pthread_mutex_lock(&mutex); // 获取互斥锁if (tickets > 0) {printf("Thread %ld sold ticket: %d\n", (long)arg, tickets);tickets--;pthread_mutex_unlock(&mutex); // 释放互斥锁} else {pthread_mutex_unlock(&mutex); // 释放互斥锁break; // 如果没有票了,退出循环}// 模拟线程间的切换,增加并发冲突的可能性// 可以通过注释掉下面这行代码来查看效果usleep(100); // 线程休眠100微秒}return NULL;
}int main() {pthread_t threads[NUM_THREADS];// 初始化互斥锁if (pthread_mutex_init(&mutex, NULL) != 0) {printf("Mutex init failed\n");return -1;}// 创建两个线程for (int i = 0; i < NUM_THREADS; i++) {if (pthread_create(&threads[i], NULL, sell_tickets, (void*)(intptr_t)(i + 1)) != 0) {printf("Thread creation failed\n");return -1;}}// 等待所有线程完成for (int i = 0; i < NUM_THREADS; i++) {pthread_join(threads[i], NULL);}// 销毁互斥锁pthread_mutex_destroy(&mutex);printf("All tickets have been sold.\n");return 0;
}

代码解释

  • pthread_mutex_t mutex;:定义了一个互斥锁变量。
  • pthread_mutex_lock(&mutex);pthread_mutex_unlock(&mutex);:分别用于获取和释放互斥锁。当一个线程获取到互斥锁后,其他试图获取同一互斥锁的线程将被阻塞,直到第一个线程释放该锁。
  • usleep(100);:让当前线程暂停一段时间,模拟线程间的时间片轮转,以便观察到并发冲突的情况。如果注释掉这一行,可能会减少并发冲突的机会,因为线程可能在执行完一次卖票操作前不会被打断。
  • pthread_create(&threads[i], NULL, sell_tickets, (void*)(intptr_t)(i + 1));:创建新线程并开始执行sell_tickets函数。

通过这个例子,您可以清楚地看到如何在线程之间安全地共享资源以及如何避免竞态条件。请注意,在实际应用中,需要根据具体情况调整互斥锁的应用范围,以最小化锁的粒度,从而提高程序的效率。
好的!我将通过一个详细的嵌入式案例,帮助你理解 线程创建、同步互斥和互斥锁 的概念。我们将使用 POSIX 线程(pthread)库 在 Linux 环境下实现,这是嵌入式开发中常用的多线程编程方法。


案例背景2

假设我们有一个嵌入式系统,需要同时采集传感器数据(如温度)和进行数据处理。为了避免数据竞争,我们需要:

  1. 创建两个线程:一个负责采集数据,另一个负责处理数据。
  2. 使用 互斥锁(Mutex) 保护共享数据(传感器数据)。

代码实现

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>  // 用于 sleep 函数// 定义共享数据
int sensor_data = 0;// 定义互斥锁
pthread_mutex_t mutex;// 线程1:采集传感器数据
void *sensor_thread(void *arg) {while (1) {pthread_mutex_lock(&mutex);  // 加锁sensor_data++;               // 修改共享数据printf("Sensor采集数据: %d\n", sensor_data);pthread_mutex_unlock(&mutex);  // 解锁sleep(1);  // 模拟采集耗时}return NULL;
}// 线程2:处理传感器数据
void *process_thread(void *arg) {while (1) {pthread_mutex_lock(&mutex);  // 加锁int data = sensor_data;     // 读取共享数据printf("Process处理数据: %d\n", data);pthread_mutex_unlock(&mutex);  // 解锁sleep(2);  // 模拟处理耗时}return NULL;
}int main() {pthread_t tid1, tid2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建两个线程pthread_create(&tid1, NULL, sensor_thread, NULL);pthread_create(&tid2, NULL, process_thread, NULL);// 等待线程结束(实际中可能需要信号量控制)pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 销毁互斥锁pthread_mutex_destroy(&mutex);return 0;
}

代码解析

  1. 共享数据与互斥锁

    • sensor_data 是共享的全局变量,会被两个线程同时访问。
    • pthread_mutex_t mutex 定义了一个互斥锁,用于保护对 sensor_data 的访问。
  2. 线程函数

    • sensor_thread:模拟传感器数据采集,每次对 sensor_data 加1。
    • process_thread:模拟数据处理,读取 sensor_data 的值。
    • 在访问共享数据前,通过 pthread_mutex_lock 加锁;操作完成后,通过 pthread_mutex_unlock 解锁。
  3. 主函数

    • 初始化互斥锁 pthread_mutex_init
    • 创建两个线程 pthread_create
    • 等待线程结束 pthread_join(实际项目中可能需要更复杂的同步机制)。

关键概念总结

  1. 互斥锁(Mutex)

    • 用于保护共享资源,确保同一时间只有一个线程访问。
    • 操作:lock(加锁) → 临界区操作 → unlock(解锁)。
  2. 线程同步

    • 通过互斥锁协调多个线程的执行顺序,避免数据竞争(Data Race)。
  3. 嵌入式场景注意事项

    • 避免死锁:确保加锁后一定会解锁。
    • 最小化临界区:减少锁的持有时间,提高系统实时性。

扩展练习

  1. 尝试移除互斥锁,观察数据不一致的现象。
  2. 添加第三个线程(如数据上传线程),进一步练习多线程同步。
  3. 研究其他同步机制(如信号量、条件变量)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/73232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot第二天

目录 1.Web开发 1.1简介 1.2SpringBoot对静态资源的映射规则 1.3模板引擎 1.3.1引入thymeleaf&#xff1b; 1.3.2Thymeleaf语法 1.3.2.1标准表达式语法 1.变量表达式 1.3.2.2表达式支持的语法 1.3.2.3常用的thymeleaf标签 1.4Springboot整合springmvc 1.4.1Springmvc…

Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级

一、缓存雪崩&#xff1a; 1、什么是缓存雪崩&#xff1a; 如果缓在某一个时刻出现大规模的key失效&#xff0c;那么就会导致大量的请求打在了数据库上面&#xff0c;导致数据库压力巨大&#xff0c;如果在高并发的情况下&#xff0c;可能瞬间就会导致数据库宕机。这时候如果…

Html5记忆翻牌游戏开发经验分享

H5记忆翻牌游戏开发经验分享 这里写目录标题 H5记忆翻牌游戏开发经验分享前言项目概述技术要点解析1. 页面布局&#xff08;HTML CSS&#xff09;响应式设计 2. 翻牌动画效果3. 游戏逻辑实现状态管理卡片配对检测 开发技巧总结1. 模块化设计2. 性能优化3. 用户体验 踩坑经验扩…

【开源+代码解读】Search-R1:基于强化学习的检索增强大语言模型框架3小时即可打造个人AI-search

大语言模型(LLMs)在处理复杂推理和实时信息检索时面临两大挑战:知识局限性(无法获取最新外部知识)和检索灵活性不足(传统方法依赖固定检索流程)。现有方法如检索增强生成(RAG)和工具调用(Tool-Use)存在以下问题: RAG:单轮检索导致上下文不足,无法适应多轮交互场景…

Linux网络套接字编程——创建并绑定

目录 网络字节序 socket编程接口 socket bind 如果将进程比作一个房子&#xff0c;那套接字相当于是一扇门&#xff0c;通向与外界通信的通道。 在网络中&#xff0c;如何理解套接字呢&#xff0c;时刻记住套接字是为了标识互联网中的某一台主机上的某一个进程&#xff0c…

1720. 解码异或后的数组

解码异或后的数组 题目描述尝试做法 题目描述 未知整数数组 arr 由 n 个非负整数组成。 经编码后变为长度为 n - 1 的另一个整数数组 encoded &#xff0c;其中 encoded[i] arr[i] XOR arr[i 1] 。例如&#xff0c;arr [1,0,2,1] 经编码后得到 encoded [1,2,3] 。 给你编…

了解一下HTTP的短连接和长连接

在 HTTP 协议中&#xff0c;连接的方式主要分为长连接和短连接。这两种连接方式的主要区别在于连接的生命周期和数据传输的效率。理解它们的差异对于优化 Web 应用的性能和资源利用至关重要。以下是 HTTP 长连接和短连接的详细解释。 1. 短连接&#xff08;HTTP/1.0&#xff0…

【WRF模拟】如何查看 WPS 的输入静态地理数据(二进制格式)?

查看 WPS 的输入静态地理数据方法总结 方法 1:使用 gdal_translate 将二进制数据转换为 GeoTIFFgdal_translate 工具概述使用 gdal_translate 将二进制数据转换为 GeoTIFF方法 2:使用 ncdump 查看 geo_em.dXX.nc方法 3:使用 Python xarray + matplotlib 可视化 geo_em.dXX.n…

Mybatis语法bug

select * from appointment where status ‘ACCEPTED’ and expire_time< now() idea显示now&#xff08;&#xff09;这里一直报错&#xff1a; 应为标记名称 应为 Deepseek: 根据您的代码和报错信息分析&#xff0c;这是一个 MyBatis XML 文件中的 SQL 语法问题。具体原…

DeepSeek本机部署(基于Ollama和Docker管理)

目录 一、ollama 与 docker 简介 &#xff08;一&#xff09;ollama(Ollama) &#xff08;二&#xff09;docker 二、利用 ollama 和 docker 配置 deepseek-r1 的准备工作 &#xff08;一&#xff09;硬件需求 &#xff08;二&#xff09;软件安装 三、配置 deepseek-r1…

小程序 wxml 语法 —— 39 简单双向数据绑定

在 WXML 中&#xff0c;普通属性的绑定是单向的&#xff0c;比如 <input value"{{ value }}" />&#xff0c;当数据发生改变时&#xff0c;页面也会随之发生变化&#xff0c;但是当用户在输入框中输入最新内容&#xff0c;最新内容并不会同步给 value 数据&…

Linux第一次练习

1、找到你的Linux系统上的不同颜色的文件&#xff0c;每一种颜色的文件找到3个以上 蓝色&#xff1a; 白色&#xff1a; 绿色&#xff1a; 红色&#xff1a; 黄色&#xff1a; 2、设置一个ping的别名永久生效&#xff0c;设置一个ymd的别名date %F永久生效

《C#上位机开发从门外到门内》2-2:I2C总线协议及其应用详解

文章目录 一、引言二、I2C总线协议的基本概念三、I2C通信机制3.1 硬件结构与基本原理3.2 信号的起始与终止3.3 数据传输格式及时序3.4 时钟同步与时钟伸展 四、设备寻址与数据传输4.1 I2C设备寻址方式4.2 地址冲突及解决方法4.3 数据传输过程中的确认机制4.4 I2C数据帧结构与传…

Trae IDE:解锁 AI 驱动的高效编程体验

Trae 介绍 Trae 是字节跳动推出的一款面向开发者的 AI 驱动的集成开发环境&#xff08;IDE&#xff09;&#xff0c;于 2024 年 1 月 19 日在新加坡正式发布海外版&#xff0c;2025 年 3 月 3 日发布国内版。海外版由字节跳动旗下的 SPRING&#xff08;SG&#xff09;PTE.LTD.…

玩转python:通俗易懂掌握高级数据结构:collections模块之namedtuple

引言 namedtuple是Python中collections模块提供的一个强大工具&#xff0c;用于创建具有字段名的元组。它不仅具备元组的不可变性&#xff0c;还能通过字段名访问元素&#xff0c;极大地提高了代码的可读性和可维护性。本文将详细介绍namedtuple的关键用法和特性&#xff0c;并…

我的创作纪念日:730天的技术写作之旅

我的创作纪念日&#xff1a;730天的技术写作之旅 机缘 从一篇案例分析开始 2023年3月13日&#xff0c;我写下了第一篇技术博客《软考高级-系统分析师-案例分析-系统维护与设计模式》。那时的初心很简单&#xff1a; 沉淀实战经验——在备考软考系统分析师时&#xff0c;发现…

使用 Arduino 和 ESP8266 Wi-Fi 模块发送电子邮件

使用 Arduino Uno 和 ESP8266 Wi-Fi 模块发送电子邮件 我们正在迈向物联网 (IoT) 世界。这项技术在电子和嵌入式系统中起着非常重要的作用。从任何微控制器或嵌入式系统发送电子邮件都是非常基本的事情,这在 IoT 中是必需的。因此,在本文中,我们将学习“如何使用 Wi-Fi 和…

golang算法二叉树对称平衡右视图

100. 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a…

c++介绍智能指针 十二(1)

普通指针&#xff1a;指向内存区域的地址变量。使用普通指针容易出现一些程序错误。 如果一个指针所指向的内存区域是动态分配的&#xff0c;那么这个指针变量离开了所在的作用域&#xff0c;这块内存也不会自动销毁。动态内存不进行释放就会导致内存泄露。如果一个指针指向已…

亚马逊COSMO算法解读:新搜索时代的流量分配与DeepBI AI驱动的智能优化策略

亚马逊COSMO算法的推出&#xff0c;标志着其搜索和推荐系统进入了智能化、个性化的新阶段。该算法通过分析用户购物习惯、搜索历史、浏览行为等数据&#xff0c;为买家提供精准推荐&#xff0c;同时对卖家的运营策略提出了更高的要求。在这一背景下&#xff0c;AI驱动的DeepBI能…