c++手撕线程池

C++手撕线程池


#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>#define LL_ADD(item, list) do{			 \item->prev = NULL;					 \item->next = list;					 \if (list != NULL) list-> prev = item;\list = item;						 \
} while(0) #define LL_REMOVE(item, list) do{ 								 \if(item-> prev != NULL) item -> prev -> next = item -> next; \if(item-> next != NULL) item -> next -> prev = item -> prev; \	if(list == item) list = item -> next;						 \item-> prev = item -> next = NULL;   						 \
} while(0) struct NJOB
{void (*func)(void *arg); // 回调函数每一个任务需要那些参数void *user_data; // 任务执行需要什么参数struct NJOB *prev;struct NJOB *next; //双向链表 队列
};
// 若干任务组织到一起struct NWORKER
{pthread_t id; // 每一个worker相当于一个线程int terminate;struct NMANAGER *pool;struct NWORKER *prev;struct NWORKER *next;};typedef struct NMANAGER
{pthread_mutex_t mtx; //用于抓取任务pthread_cond_t cond; //当任务队列为空的时候做的struct NJOB *jobs;struct NWORKER *workers;	
} nThreadPool;void *thread_cb(void *arg)
{struct NWORKER *worker = (struct NWORKER*) arg;// 线程从队列里面拿到一个任务去执行// 线程去争夺队列里面的资源// 不断去执行,是一个循环while(1){// 具体内容是由job的回调函数决定的pthread_mutex_lock(&worker->pool->mtx);while(worker->pool->jobs == NULL){if (worker-> terminate) break;pthread_cond_wait(&worker->pool->cond, &worker->pool-> mtx);}// 如果job为空就让线程休息// 检查终止条件if (worker->terminate) {pthread_mutex_unlock(&worker->pool->mtx);break;}struct NJOB *job = worker-> pool-> jobs;if(job != NULL){LL_REMOVE(job, worker-> pool-> jobs); // 取出头结点}pthread_mutex_unlock(&worker->pool->mtx);// 执行任务if (job != NULL && job -> func != NULL){job-> func(job->user_data);// 注意:这里不释放job,由任务函数负责释放user_datafree(job);// 释放任务结构体本身}}
}// sdkint nThreadPoolCreate(nThreadPool *pool, int numWorkers)
{// if (numWorkers < 1) numWorkers = 1;if (pool == NULL) return -1;memset(pool, 0, sizeof(nThreadPool));//pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;memcpy(&pool->mtx, &blank_mutex, sizeof(pthread_mutex_t));// pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));//int i = 0;for(i =0;i<numWorkers;i++){struct NWORKER *worker = (struct NWORKER *)malloc(sizeof(struct NWORKER));if (worker == NULL){perror("malloc");return -2;}memset(worker, 0 ,sizeof(struct NWORKER) );worker->pool = pool;pthread_create(&worker->id, NULL, thread_cb,worker);LL_ADD(worker, pool->workers);}return 0;
}int nThreadPoolDestroy(nThreadPool *pool)
{if(pool == NULL)return -1;// 设置所有worker的终止标志struct NWORKER *worker = pool-> workers;while (worker != NULL){worker->terminate = 1;worker = worker -> next;}// 广播条件变量唤醒所有线程pthread_mutex_lock(&pool->mtx);pthread_cond_broadcast(&pool->cond);pthread_mutex_unlock(&pool->mtx);// 等待所有worker线程退出worker = pool->workers;while (worker != NULL) {pthread_join(worker->id, NULL);struct NWORKER *next = worker->next;free(worker);worker = next;}// 销毁互斥锁和条件变量pthread_mutex_destroy(&pool-> mtx);pthread_cond_destroy(&pool->cond);// 清空任务队列(如果有剩余任务)struct NJOB *job = pool->jobs;while (job!= NULL){struct NJOB *next = job->next;free(job);job = next;}// 重置线程池状态pool-> workers = NULL;pool->jobs = NULL;return 0;}int nThreadPoolPushTask(nThreadPool *pool, struct NJOB *job)
{//如何往线程池丢任务// 往队列里面加一个节点(job)// 同时通知一个worker(线程)// 因为线程都在条件等待pthread_mutex_lock(&pool->mtx);LL_ADD(job, pool->jobs);pthread_cond_signal(pool->cond);pthread_mutex_unlock(&pool->mtx);
}// // 测试函数
void debug_test()
{nThreadPool pool;const int NUM_THREADS = 10;const int NUM_TASKS = 1000;printf("Creating thread pool with %d workers...\n", NUM_THREADS);if (nThreadPoolCreate(&pool, NUM_THREADS) != 0) {fprintf(stderr, "Failed to create thread pool\n");return;}printf("Adding %d tasks to the pool...\n", NUM_TASKS);srand(time(NULL));for (int i = 0; i < NUM_TASKS; i++) {struct NJOB *job = (struct NJOB *)malloc(sizeof(struct NJOB));TaskData *data = (TaskData *)malloc(sizeof(TaskData));data->task_id = i;data->value = rand() % 100; // 随机值0-99job->func = task_function;job->user_data = data;nThreadPoolPushTask(&pool, job);}// 等待所有任务完成printf("Waiting for tasks to complete...\n");sleep(3); // 简单等待3秒// 销毁线程池printf("Destroying thread pool...\n");nThreadPoolDestroy(&pool);printf("All done!\n");
}#if 1  //#if 0是完美注释 #if 1即可运行
int main()
{debug_test();return 0;
}
#endif

互斥锁

互斥锁(Mutex)详解

互斥锁(Mutual Exclusion Lock,简称 Mutex)是多线程编程中用于保护共享资源不被同时访问的核心同步机制。它的核心作用是确保在任何给定时刻,只有一个线程可以访问被保护的资源或代码区域。

互斥锁的核心概念

1. 基本工作原理

线程1请求锁
锁空闲?
获取锁
等待
访问共享资源
释放锁

2. 关键特性

  • 互斥性:同一时刻只允许一个线程持有锁
  • 原子性:锁的获取和释放操作是原子的(不可中断)
  • 阻塞性:未获得锁的线程会进入等待状态
  • 所有权:锁由获取它的线程独占持有

互斥锁的使用场景

场景类型示例互斥锁的作用
数据竞争多个线程访问同一变量防止并发修改导致数据不一致
临界区保护文件I/O操作确保操作序列的完整性
资源管理数据库连接池控制有限资源的分配
状态同步缓存更新机制保持状态的一致性

互斥锁的操作原语(以POSIX为例)

基本操作流程

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 线程函数
void* thread_func(void* arg) {pthread_mutex_lock(&mutex);    // 1. 尝试获取锁// 临界区代码 - 安全访问共享资源pthread_mutex_unlock(&mutex);  // 2. 释放锁return NULL;
}

锁的类型

锁类型特性适用场景
普通锁默认类型,不可递归获取简单临界区保护
递归锁同一线程可多次获取递归函数或可重入代码
检错锁检测死锁和重复锁定调试阶段
自适应锁优化自旋等待时间高竞争环境

互斥锁的底层实现原理

1. 硬件支持

现代CPU提供原子指令实现锁:

  • Test-and-Set 指令
  • Compare-and-Swap (CAS) 指令
  • Load-Link/Store-Conditional (LL/SC) 指令

2. 用户空间实现(Futex)

Linux的Futex(Fast Userspace Mutex)机制:

// 简化版Futex实现
void futex_lock(int* lock) {while (atomic_compare_exchange(lock, 0, 1) != 0) {// 锁已被占用,进入内核等待syscall(SYS_futex, lock, FUTEX_WAIT, 1, NULL, NULL, 0);}
}void futex_unlock(int* lock) {atomic_set(lock, 0);// 唤醒等待线程syscall(SYS_futex, lock, FUTEX_WAKE, 1, NULL, NULL, 0);
}

3. 内核支持

当锁竞争激烈时,操作系统内核介入:

  1. 将等待线程放入阻塞队列
  2. 在锁释放时调度等待线程
  3. 处理优先级反转问题

互斥锁的使用注意事项

1. 避免死锁

死锁产生的四个必要条件:

  1. 互斥条件
  2. 持有并等待
  3. 不可抢占
  4. 循环等待

预防策略

  • 固定加锁顺序
  • 使用超时机制
  • 死锁检测算法

2. 性能优化

技术描述效果
细粒度锁为不同资源使用独立锁减少竞争
读写锁区分读/写操作提高读并发性
无锁编程CAS操作替代锁完全避免锁开销
锁分段将大资源分段加锁减少热点竞争

3. 最佳实践

// 推荐用法:RAII模式(资源获取即初始化)
void critical_section() {LockGuard guard(&mutex); // 构造时加锁// 临界区代码// ...} // 析构时自动解锁(即使发生异常)

互斥锁与其他同步机制对比

机制特点适用场景
互斥锁独占访问,简单直接通用临界区保护
自旋锁忙等待,不放弃CPU锁持有时间极短
信号量计数器,控制访问数量资源池管理
条件变量基于条件等待/通知生产者-消费者模式
读写锁区分读/写访问权限读多写少场景

互斥锁在不同语言中的实现

C++ (std::mutex)

#include <mutex>std::mutex mtx;void safe_increment(int& value) {std::lock_guard<std::mutex> lock(mtx);value++;
}

Java (synchronized)

public class Counter {private int value;private final Object lock = new Object();public void increment() {synchronized(lock) {value++;}}
}

Python (threading.Lock)

import threadinglock = threading.Lock()
counter = 0def increment():global counterwith lock:counter += 1

互斥锁的性能考量

性能指标

  • 获取延迟:从请求到获取锁的时间
  • 吞吐量:单位时间处理的操作数
  • 可扩展性:随着线程数增加的性能变化

优化策略

  1. 减少临界区大小:最小化加锁代码范围
  2. 使用本地副本:在临界区外准备数据
  3. 锁消除:分析确定不需要的锁
  4. 锁粗化:合并相邻的锁操作

总结

互斥锁是并发编程的基石,核心价值在于:

  • 提供线程安全的共享资源访问
  • 确保数据一致性
  • 维护程序正确性

正确使用互斥锁需要:

  1. 精确识别需要保护的共享资源
  2. 保持临界区尽可能短小
  3. 建立一致的加锁顺序
  4. 使用RAII模式管理锁生命周期
  5. 在高竞争场景考虑替代方案

理解互斥锁的底层原理和最佳实践,是构建高性能、高可靠性并发系统的关键基础。

C++程序从编译到变为.exe文件发生了什么

将 C++ 源代码编译为 Windows 的 .exe 文件是一个多阶段的复杂过程,涉及多个工具和转换步骤。以下是详细的编译过程:


完整的编译流程

源代码 .cpp
预处理器
编译后的代码 .ii
编译器
汇编代码 .s
汇编器
目标文件 .obj
链接器
可执行文件 .exe

详细步骤解析

1. 预处理阶段
  • 输入.cpp 源文件 + 头文件
  • 输出:预处理后的源文件(通常为 .i.ii
  • 处理工具:预处理器(如 cpp 或编译器内置预处理器)
  • 主要任务
    • 处理所有 # 开头的预处理指令
      • #include → 插入头文件内容
      • #define → 宏替换
      • #if/#ifdef → 条件编译
    • 删除所有注释
    • 添加行号标记(用于调试)
    • 处理特殊宏(如 __LINE__, __FILE__

示例

// main.cpp
#include <iostream>
#define PI 3.14159int main() {std::cout << "Value: " << PI; // 注释
}

↓ 预处理后 ↓

// 插入<iostream>的全部内容
namespace std { ... } // 约数千行代码
#line 1 "main.cpp"
int main() {std::cout << "Value: " << 3.14159;
}

2. 编译阶段
  • 输入:预处理后的源文件(.i
  • 输出:汇编代码文件(.s.asm
  • 处理工具:编译器核心(如 g++ 的编译前端)
  • 主要任务
    • 词法分析:将源代码分解为 token(关键字、标识符、运算符等)
    • 语法分析:构建抽象语法树(AST)
    • 语义分析:类型检查、作用域分析
    • 中间代码生成:生成平台无关的中间表示(如 LLVM IR)
    • 优化:进行各种优化(常量折叠、死代码消除等)
    • 目标代码生成:生成特定CPU架构的汇编代码

生成的汇编示例(x86):

_main:push    ebpmov     ebp, esppush    OFFSET FLAT:.LC0 ; "Value: "push    3.14159call    __ZNSolsEf      ; std::cout<<float...

3. 汇编阶段
  • 输入:汇编代码文件(.s
  • 输出:目标文件(.obj.o
  • 处理工具:汇编器(如 asml
  • 主要任务
    • 将汇编指令转换为机器码
    • 生成目标文件格式(COFF 或 ELF)
    • 创建符号表(函数/变量地址)
    • 生成重定位信息(供链接器使用)

目标文件内容

  • 机器码(二进制指令)
  • 数据段(初始化的全局变量)
  • BSS 段(未初始化的全局变量)
  • 符号表
  • 重定位记录

4. 链接阶段
  • 输入:多个目标文件(.obj) + 库文件(.lib
  • 输出:可执行文件(.exe
  • 处理工具:链接器(如 ldlink.exe
  • 主要任务
    • 符号解析:解决跨文件的函数/变量引用
    • 重定位:合并所有目标文件,调整地址
    • 解析外部依赖:链接标准库和第三方库
    • 生成PE头:创建Windows可执行文件结构

链接过程关键点

main.obj
链接器
math.obj
libstdc++.lib
program.exe

PE文件结构

├── DOS头
├── PE文件头
├── 节表
├── .text节(代码)
├── .data节(初始化数据)
├── .rdata节(只读数据)
├── .bss节(未初始化数据)
├── 导入表(依赖的DLL)
├── 资源节(图标/版本信息)
└── 重定位表

完整工具链示例(GCC/MinGW)

# 1. 预处理(生成 main.ii)
g++ -E main.cpp -o main.ii# 2. 编译(生成 main.s)
g++ -S main.ii -o main.s# 3. 汇编(生成 main.obj)
as main.s -o main.obj# 4. 链接(生成 program.exe)
g++ main.obj -o program.exe

或单步完成:

g++ main.cpp -o program.exe

关键概念详解

1. 目标文件格式(COFF)
+-------------------+
| 文件头            |
+-------------------+
| 节表              |
+-------------------+
| .text 节(代码)  |
+-------------------+
| .data 节(数据)  |
+-------------------+
| .bss 节           |
+-------------------+
| 符号表            |
+-------------------+
| 重定位记录        |
+-------------------+
| 调试信息          |
+-------------------+
2. PE文件结构
+-----------------------+
| DOS Stub              |
+-----------------------+
| PE Header             |
+-----------------------+
| Section Table         |
+-----------------------+
| .text (Code)          |
+-----------------------+
| .data (Initialized)   |
+-----------------------+
| .rdata (Constants)    |
+-----------------------+
| .idata (Imports)      |
+-----------------------+
| .rsrc (Resources)     |
+-----------------------+
| .reloc (Relocations)  |
+-----------------------+
3. 动态链接 vs 静态链接
特性静态链接动态链接
文件大小较大(包含所有依赖库)较小(仅包含引用)
内存占用每个程序独立占用内存共享库节省内存
更新难度需重新编译整个程序只需替换DLL文件
加载速度较快(无运行时加载开销)较慢(需加载DLL)
示例-static 选项默认方式

常见问题诊断

1. 链接错误示例
undefined reference to `someFunction()'

原因

  • 函数声明但未定义
  • 缺少链接库
  • 名称修饰不匹配(C vs C++)
2. 运行时错误
The procedure entry point could not be located in DLL

原因

  • 运行时DLL版本不匹配
  • 缺少依赖的DLL文件
  • 导出函数签名变更
3. 调试信息
  • 编译选项-g 生成调试信息
  • 文件格式
    • Windows:PDB(Program Database)
    • Linux:DWARF
  • 调试工具
    • Windows:WinDbg, Visual Studio Debugger
    • Linux:GDB

优化技术

源代码
编译器优化
链接时代码生成 LTCG
PGO优化
  1. 编译器优化

    • -O1:基础优化(死代码消除)
    • -O2:中级优化(循环展开)
    • -O3:激进优化(向量化)
  2. LTO(链接时优化)

    • 跨模块内联
    • 全局数据流分析
    • 使用:g++ -flto
  3. PGO(性能引导优化)

    带插桩的编译
    运行训练数据
    收集性能数据
    使用数据重新编译

总结

从 C++ 源代码到 .exe 文件的转换过程:

  1. 预处理:文本级转换(宏/头文件展开)
  2. 编译:高级语言 → 汇编语言
  3. 汇编:汇编语言 → 机器码(目标文件)
  4. 链接:合并目标文件 + 解析外部依赖 → 可执行文件

理解这个过程有助于:

  • 诊断编译/链接错误
  • 优化构建性能
  • 进行底层调试
  • 创建高效的构建系统
  • 理解程序的内存布局和执行原理

pthread库的常见用法以及使用函数

pthread_t
pthread_mutex_t
pthread_cond_t
pthread_mutex_lock
pthread_mutex_unlock
pthread_cond_wait
pthread_create
pthread_cond_broadcast
pthread_join
pthread_mutex_destroy
pthread_cond_destroy
pthread_cond_signal
pthread_mutex_lock
pthread_mutex_unlock
所有用到的pthread函数

pthread 库详解:常见用法与核心函数指南

一、pthread 库概述

POSIX Threads (pthread) 是 Unix/Linux 系统上实现多线程编程的标准 API,提供了一套完整的线程创建、管理和同步机制。

核心特性:

  • 跨平台:支持所有 POSIX 兼容系统(Linux, macOS, BSD 等)
  • 轻量级:线程创建和切换开销远小于进程
  • 共享内存:同一进程的线程共享地址空间
  • 丰富同步机制:提供互斥锁、条件变量、读写锁等

编译链接:

gcc -pthread program.c -o program

二、核心线程管理函数

1. 线程创建 - pthread_create()

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);
  • thread:输出参数,存储新线程ID
  • attr:线程属性(NULL 为默认属性)
  • start_routine:线程入口函数
  • arg:传递给线程函数的参数

示例

void *print_message(void *message) {printf("%s\n", (char*)message);return NULL;
}int main() {pthread_t tid;char *msg = "Hello from thread!";pthread_create(&tid, NULL, print_message, msg);pthread_join(tid, NULL); // 等待线程结束return 0;
}

2. 线程终止 - pthread_exit()

void pthread_exit(void *retval);
  • 用于线程内部主动退出
  • retval:线程返回值(可由 pthread_join 获取)

3. 线程等待 - pthread_join()

int pthread_join(pthread_t thread, void **retval);
  • 阻塞调用线程,直到目标线程结束
  • retval:接收目标线程的返回值

4. 线程分离 - pthread_detach()

int pthread_detach(pthread_t thread);
  • 将线程标记为"可分离",结束后自动回收资源
  • 分离线程不能被 join

5. 线程取消 - pthread_cancel()

int pthread_cancel(pthread_t thread);
  • 请求取消指定线程
  • 目标线程需要设置取消点(如 sleep, wait 等)

三、线程同步机制

1. 互斥锁 (Mutex)

// 初始化(静态)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 初始化(动态)
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);// 加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);// 尝试加锁(非阻塞)
int pthread_mutex_trylock(pthread_mutex_t *mutex);// 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);// 销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

使用模式

pthread_mutex_t lock;void *thread_func(void *arg) {pthread_mutex_lock(&lock);// 临界区代码pthread_mutex_unlock(&lock);return NULL;
}

2. 条件变量 (Condition Variables)

// 初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);// 等待条件
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);// 定时等待
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);// 发送信号(唤醒一个等待线程)
int pthread_cond_signal(pthread_cond_t *cond);// 广播信号(唤醒所有等待线程)
int pthread_cond_broadcast(pthread_cond_t *cond);// 销毁
int pthread_cond_destroy(pthread_cond_t *cond);

生产者-消费者示例

pthread_mutex_t lock;
pthread_cond_t cond;
int count = 0;void *producer(void *arg) {while(1) {pthread_mutex_lock(&lock);count++;printf("Produced: %d\n", count);pthread_cond_signal(&cond); // 唤醒消费者pthread_mutex_unlock(&lock);sleep(1);}
}void *consumer(void *arg) {while(1) {pthread_mutex_lock(&lock);while(count == 0) {pthread_cond_wait(&cond, &lock); // 等待生产}count--;printf("Consumed: %d\n", count);pthread_mutex_unlock(&lock);}
}

3. 读写锁 (Read-Write Lock)

// 初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;// 读锁定(共享)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);// 写锁定(独占)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);// 解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

适用场景

  • 读操作远多于写操作
  • 允许多个读线程同时访问

四、线程属性控制

1. 初始化/销毁属性

int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);

2. 常用属性设置

// 设置分离状态
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
// PTHREAD_CREATE_JOINABLE (默认) / PTHREAD_CREATE_DETACHED// 设置栈大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);// 设置调度策略
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
// SCHED_FIFO, SCHED_RR, SCHED_OTHER// 设置调度参数
int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);

示例

pthread_attr_t attr;
pthread_attr_init(&attr);// 设置为分离线程
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 设置栈大小为2MB
pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);pthread_t tid;
pthread_create(&tid, &attr, thread_func, NULL);pthread_attr_destroy(&attr);

五、线程特定数据 (Thread-Specific Data)

允许每个线程拥有自己的私有数据副本

// 创建键
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));// 删除键
int pthread_key_delete(pthread_key_t key);// 设置键的值
int pthread_setspecific(pthread_key_t key, const void *value);// 获取键的值
void *pthread_getspecific(pthread_key_t key);

示例

pthread_key_t tls_key;void destructor(void *value) {free(value);
}void init_tls() {pthread_key_create(&tls_key, destructor);
}void *thread_func(void *arg) {// 为每个线程分配私有数据int *data = malloc(sizeof(int));*data = pthread_self(); // 示例值pthread_setspecific(tls_key, data);// 获取私有数据int *my_data = pthread_getspecific(tls_key);printf("Thread %lu: my data = %d\n", (unsigned long)pthread_self(), *my_data);return NULL;
}

六、高级同步机制

1. 屏障 (Barriers)

// 初始化
int pthread_barrier_init(pthread_barrier_t *barrier,const pthread_barrierattr_t *attr,unsigned int count);// 等待
int pthread_barrier_wait(pthread_barrier_t *barrier);// 销毁
int pthread_barrier_destroy(pthread_barrier_t *barrier);

使用场景:多线程同步点,所有线程到达屏障前阻塞

2. 自旋锁 (Spinlocks)

// 初始化
pthread_spinlock_t spinlock;
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);// 加锁/解锁
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

特点:忙等待锁,适用于锁持有时间极短的场景

七、最佳实践与常见陷阱

1. 线程安全设计原则

  • 优先使用不可变数据结构
  • 限制共享数据范围
  • 使用RAII模式管理锁
  • 避免锁嵌套(如必须,保持固定顺序)
  • 优先使用高级并发抽象(线程池、任务队列)

2. 常见错误

// 错误1:返回栈上变量的指针
void *thread_func(void *arg) {int local = 42;return &local; // 错误!线程退出后局部变量被销毁
}// 错误2:忘记解锁
void critical_section() {pthread_mutex_lock(&lock);// 可能提前返回或抛出异常if(error) return; // 忘记解锁!pthread_mutex_unlock(&lock);
}// 错误3:虚假唤醒处理不当
while(!condition) { // 必须用循环检查条件pthread_cond_wait(&cond, &lock);
}

3. 调试技巧

  • 使用 gdbinfo threads 查看线程状态
  • 使用 valgrind --tool=helgrind 检测数据竞争
  • 添加调试日志(带线程ID):
    printf("[Thread %lu] %s\n", (unsigned long)pthread_self(), message);
    

八、完整示例:线程池实现

#include <pthread.h>
#include <stdlib.h>#define THREAD_POOL_SIZE 4typedef struct {void (*task)(void *);void *arg;
} Task;typedef struct {pthread_mutex_t lock;pthread_cond_t task_cond;pthread_t threads[THREAD_POOL_SIZE];Task *task_queue;int queue_size;int queue_capacity;int shutdown;
} ThreadPool;void *worker_thread(void *arg) {ThreadPool *pool = (ThreadPool *)arg;while(1) {pthread_mutex_lock(&pool->lock);// 等待任务或关闭信号while(pool->queue_size == 0 && !pool->shutdown) {pthread_cond_wait(&pool->task_cond, &pool->lock);}if(pool->shutdown) {pthread_mutex_unlock(&pool->lock);pthread_exit(NULL);}// 取出任务Task task = pool->task_queue[--pool->queue_size];pthread_mutex_unlock(&pool->lock);// 执行任务task.task(task.arg);}return NULL;
}void thread_pool_init(ThreadPool *pool) {pthread_mutex_init(&pool->lock, NULL);pthread_cond_init(&pool->task_cond, NULL);pool->queue_capacity = 10;pool->queue_size = 0;pool->task_queue = malloc(pool->queue_capacity * sizeof(Task));pool->shutdown = 0;for(int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_create(&pool->threads[i], NULL, worker_thread, pool);}
}void thread_pool_submit(ThreadPool *pool, void (*task)(void *), void *arg) {pthread_mutex_lock(&pool->lock);// 扩展任务队列(如果需要)if(pool->queue_size == pool->queue_capacity) {pool->queue_capacity *= 2;pool->task_queue = realloc(pool->task_queue, pool->queue_capacity * sizeof(Task));}// 添加任务pool->task_queue[pool->queue_size].task = task;pool->task_queue[pool->queue_size].arg = arg;pool->queue_size++;pthread_cond_signal(&pool->task_cond);pthread_mutex_unlock(&pool->lock);
}void thread_pool_shutdown(ThreadPool *pool) {pthread_mutex_lock(&pool->lock);pool->shutdown = 1;pthread_cond_broadcast(&pool->task_cond);pthread_mutex_unlock(&pool->lock);for(int i = 0; i < THREAD_POOL_SIZE; i++) {pthread_join(pool->threads[i], NULL);}free(pool->task_queue);pthread_mutex_destroy(&pool->lock);pthread_cond_destroy(&pool->task_cond);
}

九、性能调优建议

  1. 线程数量优化

    • CPU密集型:线程数 ≈ CPU核心数
    • I/O密集型:线程数 ≈ (等待时间/计算时间 + 1) × CPU核心数
    • 使用 sysconf(_SC_NPROCESSORS_ONLN) 获取CPU核心数
  2. 锁竞争优化

    • 减小临界区范围
    • 使用读写锁替代互斥锁
    • 尝试无锁数据结构(atomic操作)
    • 使用线程本地存储减少共享
  3. 内存优化

    • 设置合理的线程栈大小
    • 避免false sharing(缓存行对齐)
    • 使用内存池减少动态分配

十、跨平台注意事项

  1. Windows 兼容性:

    • 使用 pthreads-win32 或 Windows Thread API
    • 关键类型对应:
      • pthread_tHANDLE
      • pthread_mutex_tCRITICAL_SECTION
  2. C++11 线程库:

    • 标准替代方案:std::thread, std::mutex
    • 优点:跨平台、类型安全
    • 缺点:功能不如 pthread 丰富

总结

pthread 库提供了强大灵活的多线程编程能力,核心要点:

  1. 理解线程生命周期管理(create/join/detach)
  2. 掌握同步机制(mutex/condvar/rwlock)
  3. 合理使用线程特定数据
  4. 遵循线程安全设计原则
  5. 重视资源管理和错误处理

通过合理使用 pthread,可以构建高性能、高并发的应用程序,充分利用多核处理器的计算能力。

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

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

相关文章

cocos creator 3.8 - 精品源码 - 六边形消消乐(六边形叠叠乐、六边形堆叠战士)

cocos creator 3.8 - 精品源码 - 六边形消消乐 游戏介绍功能介绍免费体验下载开发环境游戏截图免费体验 游戏介绍 六边形堆叠战士(六边形消消消)是一款脱胎于2048、1010&#xff0c;基于俄罗斯方块的魔性方块达人小游戏&#xff0c;可以多方向多造型消除哦&#xff01; 功能介…

3ds Max高效运行配置核心要素

要保障3ds Max流畅运行&#xff0c;需围绕计算性能、图形处理、数据吞吐三大维度构建硬件体系。不同工作环节对硬件需求存在显著差异&#xff1a; 一、核心组件配置原则 CPU&#xff1a;线程与频率双优化 建模/视口操作&#xff1a;依赖高主频&#xff08;建议≥4.0GHz&#…

实变与泛函题解-心得笔记【16】

文章目录 集合参考文献 集合 参考文献 《实变函数论与泛函分析》

道路交通标志检测数据集-智能地图与导航 交通监控与执法 智慧城市交通管理-2,000 张图像

道路交通标志检测数据集 &#x1f4e6; 已发布目标检测数据集合集&#xff08;持续更新&#xff09;&#x1f6a7; 道路交通标志检测数据集介绍&#x1f4cc; 数据集概览包含类别 &#x1f3af; 应用场景&#x1f5bc; 数据样本展示 YOLOv8 训练实战&#x1f4e6; 1. 环境配置 …

一、jenkins介绍和gitlab部署

一、jenkins介绍 jenkins和持续集成的关系 Jenkins 是实现持续集成&#xff08;CI&#xff09;最流行的自动化工具&#xff0c;它负责自动构建、测试和部署代码&#xff0c;确保团队能频繁且可靠地集成代码变更。 持续集成和敏捷开发的关系 敏捷开发是一种"快速迭代、…

k3s or kubesphere helm安装报错dial tcp 127.0.0.1:8080: connect: connection refused

在安装kubesphere时报错 Error: Kubernetes cluster unreachable: Get "http://localhost:8080/version": dial tcp 127.0.0.1:8080: connect: connection refused helm.go:92: 2025-06-27 15:14:43.30908177 0000 UTC m0.033127135 [debug] Get "http://localh…

使用datafusion和tpchgen-rs进行完整的TPCH 22个查询的基准测试

1.从源码编译bench二进制文件。 下载datafusion源码, 解压到目录&#xff0c;比如/par/dafu&#xff0c; cd /par/dafu/benchmarks export CARGO_INCREMENTAL1 export PATH/par:/par/mold240/bin:$PATH因为mold默认使用并行编译&#xff0c;而这些二进制文件很大&#xff0c;如…

【软考高项论文】论信息系统项目的干系人管理

摘要 在信息系统项目管理里&#xff0c;干系人管理极为关键&#xff0c;它不仅决定项目成败&#xff0c;还对项目进度、质量和成本有着直接影响。本文结合作者2024年6月参与管理的信息系统项目&#xff0c;详细阐述了项目干系人管理的过程&#xff0c;分析了干系人管理与沟通管…

PB应用变为Rust语言方案

从PB(PowerBuilder)迁移到现代开发软件 PowerBuilder(PB)作为早期的快速应用开发工具,曾广泛应用于企业级数据库应用开发。随着技术发展,PB逐渐面临以下挑战,促使企业转向现代开发工具: 技术陈旧与维护困难 PB的架构基于较老的客户端-服务器模式,难以适应云原生、微…

【大模型】Query 改写常见Prompt 模板

下面对常见的几种“Query 改写”Prompt 模板进行中英文对照&#xff0c;并在注释中给出中文说明&#xff0c;帮助中国用户快速理解与使用。 根据调研&#xff0c;企业级 Query 改写模块需要覆盖多种常见场景&#xff0c;包括拼写纠错、中英混合、省略上下文、多义词扩展、专业术…

西门子S7-200 SMART PLC:小型自动化领域的高效之选

在工业自动化领域&#xff0c;小型PLC作为设备控制的核心组件&#xff0c;其性能、灵活性和性价比始终是用户关注的重点。西门子推出的S7-200 SMART可编程控制器&#xff0c;凭借对中国市场需求的精准把握&#xff0c;成为了小型自动化解决方案的标杆产品。本文将从产品亮点、技…

使用iperf3测试网络的方法

深入掌握网络性能测试&#xff1a;iperf3全指南 在网络优化、故障排查和带宽验证中&#xff0c;iperf 是工程师必备的利器。这款开源工具通过模拟数据流&#xff0c;精准测量​​带宽、抖动、丢包率​​等核心指标。本文将结合实战经验&#xff0c;详解iperf的安装、参数配置和…

Level2.11继承

一、继承 #动物# #老虎、狮子、大象 #动物有共性 ##定义一个动物&#xff1a;1.有4条腿&#xff1b;2.陆地上跑&#xff1b;3.需要进食&#xff08;属性能力&#xff09; ##猫&#xff1a;同上&#xff08;继承了动物的属性和能力&#xff09; ##老鼠&#xff1a;同上#Python…

Class3Softmax回归

Class3Softmax回归 回归VS分类 回归是估计一个连续值 分类是预测一个离散类别 回归分类单连续值输出通常为多个输出自然区间R输出i是预测为第i类的置信度跟真实值的区别作为损失 生活中的分类问题 1.垃圾分类 类别&#xff1a; 可回收物 湿垃圾&#xff08;厨余垃圾&#xff0…

day042-负载均衡与web集群搭建

文章目录 0. 老男孩思想-面试官问&#xff1a;你对加班的看法?1. 负载均衡2. 搭建负载均衡的WordPress集群2.1 负载均衡服务器2.2 配置web服务器2.3 测试 踩坑记录1. /var/cache/nginx权限问题 0. 老男孩思想-面试官问&#xff1a;你对加班的看法? 互联网公司没有不加班的&a…

40岁技术人用AI寻找突破路线

年近40&#xff0c;坐标重庆&#xff0c;从事医疗器械行业多年&#xff0c;遇到发展瓶颈。刚好遇到AI技术浪潮。最近一年在不断尝试把AI应用于工作生活的方方面面。 总结一下我是如何利用AI来做职业规划的&#xff1a; 整理好自己的简历&#xff0c;越详细越好。这个可以利用…

kde截图工具报错

An error occurred while taking a screenshot. KWin screenshot request failed: The process is not authorized to take a screenshot Potentially relevant information: - Method: CaptureScreen - Method specific arguments: "eDP-2"好的&#xff0c;感谢您提…

有理函数积分——分式分解时设分解式的规则

目录 一、设前处理 1. 假式化真式 2. 分母因式分解 3. 判断可约不可约 二、一次分母 1. 多项一次分母​ 2. 单项一次重复分母​ 三、二次分母(当然是分母不可约的&#xff0c;如果可约就因式分解然后对应一次分母) 1. 多项二次分母​ 2. 单项二次重复分母​ 四、混…

从 AJAX 到 axios:前端与服务器通信实战指南

直到现在我们小宁已经更新了44作品了&#xff0c;其中和大家介绍了Python入门基础、Fast API框架、SQLite数据库&#xff0c;以及前端的知识都已经学习完了&#xff0c;总的来说现在前端、后端、数据库已经都学习了&#xff0c;那大家是否有这样的疑问&#xff0c;前端后端到底…

Pycatia二次开发基础代码解析:面属性控制、视图定向与特征统计的工业级实现

本文将以专业视角深入解析CATIA二次开发中的三个核心类方法&#xff0c;通过详细分析代码实现揭示其在工业设计中的实际应用价值。全文将严格围绕提供的代码展开&#xff0c;不做任何修改或补充。 一、面属性控制&#xff1a;精确可视化表达技术 方法功能解析 color_and_laye…