1. 线程概述
在 Linux 操作系统中,一个进程可以包含多个线程。每个线程运行在其所在进程的上下文中,有自己的栈空间。与进程一样,线程也拥有一个唯一的 ID、寄存器集合和优先级。线程间可以共享进程的资源,如打开的文件、映射的内存区域等。相对于进程,线程的创建和销毁更为轻量级,因此能够更加高效地使用系统资源,提高程序运行效率。
线程的创建和销毁都由线程库负责,常用的线程库有 pthread 和 boost::thread。其中 pthread 是 POSIX 标准中定义的线程库,是 Linux 系统下线程编程的基础。需要在编译时链接静态库 -lpthread,或者在使用时使用动态库,如下:
// 静态库链接方式
gcc -o demo demo.c -lpthread
// 动态库链接方式
gcc -o demo demo.c -l pthread
1.1 线程的创建
pthread_create 函数用于创建一个新的线程,函数的原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
其中,第一个参数是指向线程标识符的指针,第二个参数用于设置线程的属性(如栈大小、线程属性等),通常可以设为 NULL。第三个参数是线程运行的函数,第四个参数是传递给线程运行函数的参数。
在线程函数中,可以使用 pthread_self 函数获取当前线程的 ID,使用 pthread_exit 函数退出线程。在主线程中调用 pthread_join 函数可以等待指定线程结束,并回收其资源。
1.2 线程同步
在多线程环境下,线程间的执行是乱序的。因此,为了保证线程安全,需要使用线程同步手段。Linux 系统提供了多种线程同步机制,如 mutex、condition variables、semaphore 等。其中 mutex 是最基本的线程同步工具,它可以保证在任何时刻只有一个线程可以访问共享资源。mutex 可以用 pthread_mutex_t 数据类型表示,相关操作如下:
// 定义和初始化
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
// 加锁
pthread_mutex_lock(&mtx);
// 解锁
pthread_mutex_unlock(&mtx);
// 销毁
pthread_mutex_destroy(&mtx);
在实际应用中,mutex 不是一种很好的线程同步工具,因为它使用起来很容易出现死锁问题。Condition variables 是一种更高级的线程同步工具,它可以用于通知线程某个条件已经满足。相关操作如下:
// 定义和初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 线程等待
pthread_cond_wait(&cond, &mtx);
// 线程唤醒
pthread_cond_signal(&cond);
// 销毁
pthread_cond_destroy(&cond);
2. 线程池实现
线程池是一种用于控制线程的数量和生命周期的机制。线程池中包含一个或多个线程,可用于处理提交的任务,使得任务能够被异步执行。线程池的使用可以提高程序的执行效率,减少线程创建和销毁的开销。
2.1 线程池实现思路
线程池的实现需要考虑以下几个方面:
任务队列:用于存储所有提交的任务。
线程管理:用于创建和销毁工作线程。
任务调度:用于分发任务到工作线程中进行处理。
同步机制:用于保证线程安全。
下面以 C 语言为例,介绍一个简单的线程池实现。
2.2 线程池实现代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
// 线程池中的任务
typedef struct {
void *(*process)(void *);
void *arg;
} threadpool_task_t;
// 线程池结构体
typedef struct {
pthread_mutex_t lock; // 互斥锁,保护下面的变量
pthread_cond_t notify; // 条件变量,用于通知等待任务的线程
pthread_t *threads; // 工作线程数组
threadpool_task_t *queue; // 任务队列
int queue_capacity; // 任务队列容量
int queue_size; // 任务队列大小
int thread_count; // 工作线程数量
bool shutdown; // 线程池是否关闭
} threadpool_t;
// 创建线程池
threadpool_t *threadpool_create(int thread_count, int queue_capacity);
// 销毁线程池
bool threadpool_destroy(threadpool_t *pool);
// 添加任务到线程池中
bool threadpool_enqueue(threadpool_t *pool, void *(*process)(void *), void *arg);
// 启动工作线程
void *threadpool_worker(void *arg);
// 等待任务的线程
void *threadpool_waiter(void *arg);
// 创建线程池
threadpool_t *threadpool_create(int thread_count, int queue_capacity) {
threadpool_t *pool = (threadpool_t *)malloc(sizeof(threadpool_t));
pool->threads = (pthread_t *)malloc(thread_count * sizeof(pthread_t));
pool->queue = (threadpool_task_t *)malloc(queue_capacity * sizeof(threadpool_task_t));
pthread_mutex_init(&pool->lock, NULL);
pthread_cond_init(&pool->notify, NULL);
pool->queue_capacity = queue_capacity;
pool->queue_size = 0;
pool->thread_count = thread_count;
pool->shutdown = false;
// 创建工作线程
for (int i = 0; i < thread_count; i++) {
pthread_create(&pool->threads[i], NULL, threadpool_worker, (void *)pool);
}
// 创建等待任务的线程
pthread_t waiter;
pthread_create(&waiter, NULL, threadpool_waiter, (void *)pool);
return pool;
}
// 销毁线程池
bool threadpool_destroy(threadpool_t *pool) {
if (pool == NULL) {
return false;
}
pool->shutdown = true;
// 唤醒所有等待任务的线程
pthread_cond_broadcast(&pool->notify);
// 等待工作线程退出
for (int i = 0; i < pool->thread_count; i++) {
pthread_join(pool->threads[i], NULL);
}
// 释放资源
free(pool->threads);
free(pool->queue);
pthread_mutex_destroy(&pool->lock);
pthread_cond_destroy(&pool->notify);
free(pool);
return true;
}
// 添加任务到线程池中
bool threadpool_enqueue(threadpool_t *pool, void *(*process)(void *), void *arg) {
pthread_mutex_lock(&pool->lock);
// 如果任务队列已满,等待任务被取出
while (pool->queue_size == pool->queue_capacity && !pool->shutdown) {
pthread_cond_wait(&pool->notify, &pool->lock);
}
// 队列已满且线程池已关闭,返回添加失败
if (pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
return false;
}
// 添加任务到队列中
pool->queue[pool->queue_size].process = process;
pool->queue[pool->queue_size].arg = arg;
pool->queue_size++;
// 通知等待任务的线程
pthread_cond_signal(&pool->notify);
pthread_mutex_unlock(&pool->lock);
return true;
}
// 工作线程
void *threadpool_worker(void *arg) {
threadpool_t *pool = (threadpool_t *)arg;
threadpool_task_t task;
while (true) {
pthread_mutex_lock(&pool->lock);
// 等待任务到来或线程池关闭
while (pool->queue_size == 0 && !pool->shutdown) {
pthread_cond_wait(&pool->notify, &pool->lock);
}
// 线程池关闭,退出线程
if (pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
// 取出一个任务
task.process = pool->queue[0].process;
task.arg = pool->queue[0].arg;
// 将任务队列中的任务前移
for (int i = 1; i < pool->queue_size; i++) {
pool->queue[i - 1] = pool->queue[i];
}
pool->queue_size--;
// 通知等待任务的线程
pthread_cond_signal(&pool->notify);
pthread_mutex_unlock(&pool->lock);
// 执行任务
task.process(task.arg);
}
}
// 等待任务的线程
void *threadpool_waiter(void *arg) {
threadpool_t *pool = (threadpool_t *)arg;
while (true) {
pthread_mutex_lock(&pool->lock);
while (pool->queue_size != 0 || !pool->shutdown) {
pthread_cond_wait(&pool->notify, &pool->lock);
}
if (pool->shutdown) {
pthread_mutex_unlock(&pool->lock);
pthread_exit(NULL);
}
// 暂且没有任务需要执行
pthread_cond_signal(&pool->notify);
pthread_mutex_unlock(&pool->lock);
}
}
3. 总结
本文对 Linux 线程编程进行了简要介绍,包括线程的概述、线程同步、线程池的实现等内容。线程编程需要注意线程安全问题,使用同步机制保证线程安全。线程池可以提高程序的执行效率,在多线程环境下更加高效地利用系统资源。