当前位置: 首页 > news >正文

应用——线程竞争资源模型

多线程并发编程笔记:线程竞争资源模型

一、代码示例对比分析

示例1:带线程ID的窗口竞争模型

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> #include <time.h> int win = 3; // 可用窗口数 pthread_mutex_t mutex; void *th(void *arg) { int id = *(int *)arg; // 获取线程ID sleep(rand() % 5); // 随机延迟0-4秒 while (1) // 持续尝试直到成功 { pthread_mutex_lock(&mutex); if(win > 0) // 检查是否有可用窗口 { win--; // 占用一个窗口 pthread_mutex_unlock(&mutex); printf("%d:get win\n", id); // 显示获取成功 sleep(rand() % 3 + 1); // 模拟使用窗口1-3秒 pthread_mutex_lock(&mutex); win++; // 释放窗口 pthread_mutex_unlock(&mutex); printf("%d:relese win\n", id); // 显示释放 break; // 退出循环 } else { printf("%d: 没有可用窗口,等待...\n", id); pthread_mutex_unlock(&mutex); sleep(1); // 等待1秒后重试 } } return NULL; } int main(int argc, char **argv) { srand(time(NULL)); // 初始化随机种子 pthread_t tid[10]; // 线程ID数组 int id[10]; // 线程编号数组 // 初始化线程编号 for (int i = 0; i < 10; i++) { id[i] = i + 1; } pthread_mutex_init(&mutex, NULL); // 初始化互斥锁 // 创建10个线程 for (int i = 0; i < 10; i++) { pthread_create(&tid[i], NULL, th, &id[i]); } // 等待所有线程结束 for (int i = 0; i < 10; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&mutex); // 销毁互斥锁 return 0; }

示例2:匿名线程竞争模型

#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> int win = 3; // 可用窗口数 pthread_mutex_t mutex; void* th(void* arg) { while (1) { pthread_mutex_lock(&mutex); if (win > 0) // 检查是否有可用窗口 { win--; // 占用窗口 pthread_mutex_unlock(&mutex); printf("get win...\n"); // 获取成功 sleep(rand() % 5 + 1); // 使用窗口1-5秒 pthread_mutex_lock(&mutex); win++; // 释放窗口 printf("relese win...\n"); pthread_mutex_unlock(&mutex); break; // 完成任务 } else { pthread_mutex_unlock(&mutex); // 这里没有等待逻辑,会快速重试 } } return NULL; } int main(int argc, char** argv) { int i = 0; srand(time(NULL)); // 初始化随机种子 pthread_t tid[10] = {0}; // 线程ID数组 pthread_mutex_init(&mutex, NULL); // 初始化互斥锁 // 创建10个线程 for (i = 0; i < 10; i++) { pthread_create(&tid[i], NULL, th, NULL); // 不传递参数 } // 等待所有线程结束 for (i = 0; i < 10; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&mutex); // 销毁互斥锁 return 0; }

示例3:多互斥锁竞争模型(trylock实现)

#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> pthread_mutex_t mutex1; // 窗口1的锁 pthread_mutex_t mutex2; // 窗口2的锁 pthread_mutex_t mutex3; // 窗口3的锁 void* th(void* arg) { int ret = 0; while (1) { // 尝试获取窗口1 ret = pthread_mutex_trylock(&mutex1); if (0 == ret) { printf("get win1...\n"); sleep(rand() % 5 + 1); printf("release win1...\n"); pthread_mutex_unlock(&mutex1); break; } else { // 尝试获取窗口2 ret = pthread_mutex_trylock(&mutex2); if (0 == ret) { printf("get win2...\n"); sleep(rand() % 5 + 1); printf("release win2...\n"); pthread_mutex_unlock(&mutex2); break; } else { // 尝试获取窗口3 ret = pthread_mutex_trylock(&mutex3); if (0 == ret) { printf("get win3...\n"); sleep(rand() % 5 + 1); printf("release win3...\n"); pthread_mutex_unlock(&mutex3); break; } else { // 所有窗口都忙,可以添加等待逻辑 // 当前版本会忙等待 } } } } return NULL; } int main(int argc, char** argv) { int i = 0; srand(time(NULL)); pthread_t tid[10] = {0}; pthread_mutex_init(&mutex1, NULL); pthread_mutex_init(&mutex2, NULL); pthread_mutex_init(&mutex3, NULL); for (i = 0; i < 10; i++) { pthread_create(&tid[i], NULL, th, NULL); } for (i = 0; i < 10; i++) { pthread_join(tid[i], NULL); } pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); pthread_mutex_destroy(&mutex3); return 0; }

二、三种模型对比分析

1.设计思路对比

模型

核心思想

资源表示

竞争方式

模型1

集中式管理

计数器(win)

所有线程竞争一个锁,检查计数器

模型2

简化集中式

计数器(win)

同上,但无线程ID和等待消息

模型3

分布式管理

三个独立锁

每个窗口独立锁,trylock轮询

2.实现复杂度对比

模型

锁数量

代码复杂度

灵活性

模型1

1个互斥锁

中等

窗口数量易扩展

模型2

1个互斥锁

简单

功能最简

模型3

3个互斥锁

较高

每个窗口独立控制

3.性能特点对比

模型锁竞争公平性并发度
模型1高(一个锁)FIFO近似公平
模型2高(一个锁)不确定
模型3低(三个锁)优先抢占

三、关键技术点详解

1.pthread_mutex_trylock 使用

int ret = pthread_mutex_trylock(&mutex); // 返回值: // 0: 成功获取锁 // EBUSY: 锁已被占用 // 其他: 错误

2.锁的初始化与销毁

// 初始化 pthread_mutex_init(&mutex, NULL); // 或静态初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 销毁 pthread_mutex_destroy(&mutex);

3.线程创建与等待

// 创建线程 pthread_create(&tid[i], NULL, thread_func, &arg); // 等待线程结束 pthread_join(tid[i], NULL);

四、问题与改进

1.问题

示例1.2问题
Q1: 为什么线程执行顺序不确定?
  • 操作系统线程调度具有随机性

  • sleep(rand() % 5)引入随机延迟

  • 锁竞争结果不确定

Q2: 如何保证线程安全?
  • 使用互斥锁保护所有共享资源访问

  • 确保加锁和解锁配对出现

  • 避免在临界区内进行耗时操作

Q3: 为什么需要pthread_join
  • 等待线程完成,防止主线程提前退出

  • 回收线程资源

  • 确保所有任务完成

Q4: 如何优化性能?
  • 减少临界区内的操作

  • 使用读写锁(如果适用)

  • 考虑无锁数据结构

示例3的问题
  1. 忙等待问题:所有窗口都忙时,线程会快速轮询,消耗CPU

  2. 饥饿问题:可能某些线程永远抢不到窗口

  3. 优先级固定:总是先尝试窗口1,然后是2、3

2.改进方案:

// 改进版:添加随机等待和公平性 void* th_improved(void* arg) { int ret = 0; int try_count = 0; while (1) { // 随机选择尝试顺序,提高公平性 int order[3] = {0, 1, 2}; shuffle(order, 3); // 随机打乱顺序 for (int i = 0; i < 3; i++) { pthread_mutex_t* mutexes[3] = {&mutex1, &mutex2, &mutex3}; ret = pthread_mutex_trylock(mutexes[order[i]]); if (0 == ret) { printf("get win%d...\n", order[i]+1); sleep(rand() % 5 + 1); printf("release win%d...\n", order[i]+1); pthread_mutex_unlock(mutexes[order[i]]); return NULL; } } // 所有窗口都忙,指数退避等待 try_count++; int wait_time = (1 << try_count); // 指数增长 if (wait_time > 8) wait_time = 8; printf("all windows busy, waiting %d seconds...\n", wait_time); sleep(wait_time); } return NULL; }

五、应用场景建议

1.使用模型1(计数器)的场景:

  • 资源完全同质化

  • 需要精确控制资源总数

  • 扩展性要求高(窗口数量可能变化)

2.使用模型2(简化版)的场景:

  • 快速原型开发

  • 调试和演示

  • 资源竞争不激烈

3.使用模型3(多锁)的场景:

  • 资源有差异(如不同服务窗口)

  • 需要高并发性能

  • 可以接受一定的不公平性

六、最佳实践总结

  1. 根据需求选模型

    • 同质资源 → 计数器模型

    • 异质资源 → 多锁模型

    • 简单场景 → 简化模型

  2. 避免忙等待

    • trylock失败后适当sleep

    • 使用指数退避策略

    • 考虑使用条件变量

  3. 确保资源释放

    • 每个lock都要有对应的unlock

    • 所有退出路径都要释放锁

    • 主线程等待所有子线程

  4. 错误处理

    • 检查pthread函数返回值

    • 添加适当的日志输出

    • 考虑线程安全的错误处理

  5. 性能优化

    • 减少临界区大小

    • 避免在锁内进行IO操作

    • 根据竞争程度调整策略

七、扩展思考

  1. 信号量替代方案:可以使用信号量(semaphore)更简洁地实现资源计数

  2. 条件变量:使用条件变量避免忙等待,节省CPU资源

  3. 线程池:对于频繁创建销毁的场景,考虑使用线程池

这个模型是多线程编程的基础模式,广泛应用于:

  • 数据库连接池

  • 网络服务器连接管理

  • 生产者消费者问题

  • 资源池管理等场景

http://www.cnnetsun.cn/news/106392.html

相关文章:

  • 【无人船】基于模型预测控制(MPC)对USV进行自主控制研究附Matlab代码
  • 腾讯混元Video技术破局:开源130亿参数视频生成模型的创新架构与应用实践
  • GoScan终极指南:如何快速掌握交互式网络扫描利器
  • 深入理解 Java 线程池:原理、应用与最佳实践
  • Home Assistant OS 系统更新失败终极解决方案指南
  • 构建工业级ReAct智能体系统:LangGraph+MCP供应链管理全栈实现!
  • 多向量搜索技术ColBERT揭秘:提升RAG召回相关性,细粒度信息优化搜索效果!
  • 微信公众号 Markdown 编辑器,让你不再为微信内容排版
  • vue小程序基于Vue的高校心理咨询系统的设计和实现_qm264681
  • Winlator终极指南:手机运行Windows应用权限管理与性能优化完整教程
  • 10分钟极速搭建:transfer.sh私有文件分享系统全攻略
  • VR青少年法律知识学习系统|VR隔空 “解锁” 法律密码
  • coze工作流成品导入一键生成AI漫剧智能体搭建
  • 复杂工业场景如何实现3D实例与部件一体化分割?多视角贝叶斯融合的分层图像引导框
  • 【企业级Docker更新实战指南】:Agent服务无缝升级的5大黄金步骤
  • PLC通讯编程系列之一,为什么复位发送请求信号要在发送块的前面?
  • (VSCode Qiskit配置验证全流程)新手避坑指南——专家级配置实践
  • 【量子编程必备技能】:如何让VSCode完美支持Qiskit代码智能提示?
  • IDEA配置
  • Q#-Python混合调试实战指南(量子编程调试稀缺技术曝光)
  • 2026数字经济定调:数据要素成核心引擎,可信数据空间建设引行业升级
  • Vue Query Builder 终极指南:从零开始构建复杂查询界面 [特殊字符]
  • Qwen3模型推理性能优化:从思考模式到高效输出的完整指南
  • 瞄准网络安全人才缺口:大学生的机遇与成长路径
  • AI模型智能评估平台:从数据迷雾到精准决策的跨越
  • Subfinder终极指南:全面解决所有字幕下载难题
  • “负碳航空”的流行,是工业文明的一场“赎罪”与“自救”。
  • 企业数据中台建设终极指南:3步搞定数据治理难题
  • 告别繁琐!这款Mac免费Gif工具让你3步搞定屏幕录制
  • 宏智树AIPPT,用AI把学术表达变成一场轻松对话