1. Linux线程基础概念在Linux系统中线程是操作系统能够进行运算调度的最小单位它被包含在进程之中是进程中的实际运作单位。与进程相比线程更加轻量级创建和销毁的开销更小上下文切换速度更快。Linux内核从2.6版本开始采用NPTL(Native POSIX Thread Library)作为线程实现它提供了更好的线程性能和可扩展性。NPTL实现了1:1的线程模型即每个用户态线程对应一个内核调度实体。注意Linux内核并不区分进程和线程它们在内核中都被称为任务(task)。线程和进程的区别主要体现在资源共享上。1.1 线程与进程的区别虽然线程和进程都是操作系统的基本执行单元但它们之间存在几个关键差异资源共享进程拥有独立的地址空间同一进程内的线程共享地址空间和大部分资源线程可以访问进程的全局变量和堆数据创建开销创建进程需要复制父进程的资源创建线程只需分配栈空间和少量控制信息通信方式进程间通信(IPC)需要显式机制(管道、消息队列等)线程可以直接通过共享变量通信调度和切换进程切换需要切换地址空间开销大线程切换只需切换少量寄存器内容1.2 Linux线程实现模型Linux线程的实现经历了几个发展阶段LinuxThreads早期的Linux线程实现每个线程都是独立的内核调度实体存在信号处理、进程ID等问题NPTL现代Linux默认线程库1:1线程模型(每个用户线程对应一个内核线程)支持大量线程(数千个)启动速度快同步原语效率高用户态线程如GNU Portable ThreadsM:N线程模型(多个用户线程映射到少量内核线程)上下文切换快但无法利用多核2. Linux线程编程接口Linux提供了多种线程编程接口最常用的是POSIX线程(Pthreads)API。这些接口定义在pthread.h头文件中编译时需要链接pthread库(-lpthread)。2.1 线程创建与管理创建线程的基本函数是pthread_create()#include pthread.h int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);参数说明thread: 指向线程标识符的指针attr: 线程属性NULL表示默认属性start_routine: 线程执行的函数arg: 传递给线程函数的参数示例代码#include stdio.h #include pthread.h void *thread_function(void *arg) { printf(Thread is running. Argument: %s\n, (char *)arg); return NULL; } int main() { pthread_t my_thread; char *message Hello from main thread; int ret pthread_create(my_thread, NULL, thread_function, (void *)message); if (ret ! 0) { perror(pthread_create failed); return 1; } pthread_join(my_thread, NULL); printf(Thread joined\n); return 0; }2.2 线程同步机制多线程编程中最关键的问题之一是同步。Linux提供了多种同步机制互斥锁(Mutex)最基本的同步原语保护临界区防止多个线程同时访问共享资源pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; void *thread_func(void *arg) { pthread_mutex_lock(mutex); // 临界区代码 pthread_mutex_unlock(mutex); return NULL; }条件变量(Condition Variables)允许线程等待特定条件成立通常与互斥锁配合使用pthread_cond_t cond PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; void *consumer(void *arg) { pthread_mutex_lock(mutex); while (condition_not_met) { pthread_cond_wait(cond, mutex); } // 处理条件满足的情况 pthread_mutex_unlock(mutex); return NULL; } void *producer(void *arg) { pthread_mutex_lock(mutex); // 改变条件 pthread_cond_signal(cond); // 或pthread_cond_broadcast pthread_mutex_unlock(mutex); return NULL; }读写锁(Read-Write Locks)允许多个读操作同时进行写操作需要独占访问pthread_rwlock_t rwlock PTHREAD_RWLOCK_INITIALIZER; void *reader(void *arg) { pthread_rwlock_rdlock(rwlock); // 读取共享数据 pthread_rwlock_unlock(rwlock); return NULL; } void *writer(void *arg) { pthread_rwlock_wrlock(rwlock); // 修改共享数据 pthread_rwlock_unlock(rwlock); return NULL; }3. Linux线程高级特性3.1 线程属性线程创建时可以指定各种属性通过pthread_attr_t结构体控制pthread_attr_t attr; pthread_attr_init(attr); // 设置线程为分离状态 pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); // 设置栈大小 size_t stacksize 1024 * 1024; // 1MB pthread_attr_setstacksize(attr, stacksize); pthread_t thread; pthread_create(thread, attr, thread_function, NULL); pthread_attr_destroy(attr);3.2 线程局部存储线程局部存储(TLS)允许每个线程拥有变量的独立副本__thread int tls_var; // GCC扩展语法 // 或使用POSIX接口 pthread_key_t key; void destructor(void *value) { free(value); } void init_key() { pthread_key_create(key, destructor); } void *thread_func(void *arg) { int *data malloc(sizeof(int)); *data 42; pthread_setspecific(key, data); // ... free(pthread_getspecific(key)); return NULL; }3.3 线程取消Linux允许一个线程请求取消另一个线程的执行int pthread_cancel(pthread_t thread);被取消的线程可以设置取消点和取消类型// 设置取消状态 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); // 显式设置取消点 pthread_testcancel();4. Linux线程性能调优与问题排查4.1 线程数量优化确定最佳线程数量需要考虑多个因素CPU密集型任务线程数≈CPU核心数公式线程数 CPU核心数 × (1 等待时间/计算时间)I/O密集型任务可以创建更多线程但需要考虑上下文切换开销内存限制每个线程需要栈空间(默认2-10MB)大量线程可能导致内存耗尽4.2 线程问题排查工具top命令查看进程和线程的CPU、内存使用情况按H键切换线程视图ps命令ps -eLf # 查看所有线程 ps -T -p pid # 查看特定进程的线程strace跟踪线程的系统调用strace -f -p pid # 跟踪进程及其线程gdb调试调试多线程程序gdb -p pid (gdb) info threads # 查看所有线程 (gdb) thread id # 切换到指定线程4.3 常见线程问题与解决方案死锁多个线程互相等待对方持有的锁解决方案按固定顺序获取锁使用trylock和超时机制避免嵌套锁竞态条件执行结果依赖于线程调度顺序解决方案使用适当的同步机制尽量减少共享数据虚假唤醒条件变量可能在没有signal的情况下返回解决方案总是用while循环检查条件不要依赖if语句性能瓶颈锁竞争导致吞吐量下降解决方案减小临界区范围使用读写锁或无锁数据结构考虑线程局部存储5. Linux线程实践建议5.1 线程安全编程原则最小化共享数据尽可能减少线程间共享的数据量使用线程局部存储替代全局变量保持简单简单的线程模型更容易理解和维护避免过度复杂的线程交互优先使用高级抽象考虑使用线程池而不是手动管理线程使用并发库(如OpenMP、TBB)简化编程资源管理确保线程退出时释放所有资源使用RAII模式管理锁等资源5.2 线程池实现手动创建和管理大量线程效率低下线程池是更好的选择#include pthread.h #include stdlib.h #include stdio.h #define THREAD_POOL_SIZE 4 typedef struct { void (*function)(void *); void *arg; } thread_task; typedef struct { thread_task *tasks; int front, rear, count; int size; pthread_mutex_t lock; pthread_cond_t not_empty; pthread_cond_t not_full; } thread_pool_queue; typedef struct { pthread_t *threads; thread_pool_queue *queue; int shutdown; } thread_pool; void *worker_thread(void *arg) { thread_pool *pool (thread_pool *)arg; while (1) { pthread_mutex_lock(pool-queue-lock); while (pool-queue-count 0 !pool-shutdown) { pthread_cond_wait(pool-queue-not_empty, pool-queue-lock); } if (pool-shutdown) { pthread_mutex_unlock(pool-queue-lock); pthread_exit(NULL); } thread_task task pool-queue-tasks[pool-queue-front]; pool-queue-front (pool-queue-front 1) % pool-queue-size; pool-queue-count--; pthread_cond_signal(pool-queue-not_full); pthread_mutex_unlock(pool-queue-lock); (*(task.function))(task.arg); } return NULL; } thread_pool *thread_pool_create(int thread_count, int queue_size) { thread_pool *pool malloc(sizeof(thread_pool)); pool-threads malloc(sizeof(pthread_t) * thread_count); pool-queue malloc(sizeof(thread_pool_queue)); pool-queue-tasks malloc(sizeof(thread_task) * queue_size); pool-queue-size queue_size; pool-queue-front pool-queue-rear pool-queue-count 0; pthread_mutex_init(pool-queue-lock, NULL); pthread_cond_init(pool-queue-not_empty, NULL); pthread_cond_init(pool-queue-not_full, NULL); pool-shutdown 0; for (int i 0; i thread_count; i) { pthread_create(pool-threads[i], NULL, worker_thread, pool); } return pool; } void thread_pool_add_task(thread_pool *pool, void (*function)(void *), void *arg) { pthread_mutex_lock(pool-queue-lock); while (pool-queue-count pool-queue-size !pool-shutdown) { pthread_cond_wait(pool-queue-not_full, pool-queue-lock); } if (pool-shutdown) { pthread_mutex_unlock(pool-queue-lock); return; } pool-queue-tasks[pool-queue-rear].function function; pool-queue-tasks[pool-queue-rear].arg arg; pool-queue-rear (pool-queue-rear 1) % pool-queue-size; pool-queue-count; pthread_cond_signal(pool-queue-not_empty); pthread_mutex_unlock(pool-queue-lock); } void thread_pool_destroy(thread_pool *pool) { pthread_mutex_lock(pool-queue-lock); pool-shutdown 1; pthread_cond_broadcast(pool-queue-not_empty); pthread_mutex_unlock(pool-queue-lock); for (int i 0; i THREAD_POOL_SIZE; i) { pthread_join(pool-threads[i], NULL); } pthread_mutex_destroy(pool-queue-lock); pthread_cond_destroy(pool-queue-not_empty); pthread_cond_destroy(pool-queue-not_full); free(pool-queue-tasks); free(pool-queue); free(pool-threads); free(pool); }5.3 现代C中的线程C11引入了标准线程库 提供了更高级的线程抽象#include iostream #include thread #include vector #include mutex std::mutex mtx; void worker(int id) { std::lock_guardstd::mutex lock(mtx); std::cout Thread id is working\n; } int main() { std::vectorstd::thread threads; for (int i 0; i 5; i) { threads.emplace_back(worker, i); } for (auto t : threads) { t.join(); } return 0; }C还提供了其他并发工具std::async: 异步执行任务std::future/std::promise: 获取异步结果std::atomic: 原子操作std::condition_variable: 条件变量在实际项目中我通常会根据具体需求选择合适的线程模型。对于简单的并行任务C标准库提供的线程工具通常足够对于更复杂的场景可能需要考虑专门的并发库或框架。
阅读完成 · 觉得有帮助?