应用——线程竞争资源模型
2026/4/13 14:31:29 网站建设 项目流程

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

一、代码示例对比分析

示例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. 线程池:对于频繁创建销毁的场景,考虑使用线程池

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

  • 数据库连接池

  • 网络服务器连接管理

  • 生产者消费者问题

  • 资源池管理等场景

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询