一,线程简介
1)什么是线程
2)线程的实现原理简介
3)线程的优缺点
优点:
1.与进程相比,线程是一种非常“节俭”的多任务操作方式。
在Linux系统下,启动⼀个新的进程必须分配给它独⽴的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是⼀种"昂贵"的多任务⼯作⽅式。而运行于⼀个进程中的多个线程,它们彼此之间使⽤相同的地址空间,共享大部分数据,启动⼀个线程所花费的空间远远小于启动⼀个进程所花费的空间,⽽且,线程间彼此切换所需的时间也远远⼩于进程间切换所需要的时间,据统计⼀个进程的开销⼤概是⼀个线程开销的30倍左右。
2.线程间方便的通信机制
对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进⾏,这种方式不仅费时,⽽且很不⽅便。线程则不然,由于同⼀进程下的线程之间共享数据空间,所以⼀个线程的数据可以直接为其它线程所⽤,这不仅快捷,而且方便。当然,数据的共享也带来其他⼀些问题,比如线程间同步和互斥。
3.能充分利⽤多处理器的并⾏数量
CPU ⼯作的分配⼀般是以线程为级别的,只不过⼤部分程序⼀个进程只⽤⼀个线程,所以看起来好像 CPU 只能是以进程为粒度分配任务。
二,POSIX线程库函数
1)创建线程
原型:
参数:
thread:返回线程ID,pthread_t是⼀个unsigned long int类型。
2)获取线程ID

3)线程等待函数

4)线程的退出

5)线程的取消


6)线程的属性设置
其中线程属性pthread_attr_t的结构如下:
typedef struct
{
int detachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作⽤域
size_t guardsize; //线程栈末尾的警戒缓冲区⼤⼩
int stackaddr_set;
void * stackaddr; //线程栈的位置
size_t stacksize; //线程栈的⼤⼩
}pthread_attr_t;
线程的属性很多,其中着重关注的几个属性是:

设置线程是否继承调度政策:


当需要给一个线程设置调度方面的属性时,必须先将线程的inheritsched设置为PTHREAD_EXPLICIT_SCHED。

三,线程示例程序
thread.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
// 共享缓冲区
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;
// 互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
// 生产者线程函数
void* producer(void* arg) {
pthread_t tid = pthread_self();
for (int i = 1; i <= 10; i++) {
pthread_mutex_lock(&mutex);
while (count == BUFFER_SIZE) {
printf("[Producer %lu] Buffer full, waiting...\n", tid);
pthread_cond_wait(¬_full, &mutex);
}
buffer[count++] = i;
printf("[Producer %lu] Produced %d (count=%d)\n", tid, i, count);
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&mutex);
sleep(1);
}
pthread_exit("Producer finished");
}
// 消费者线程函数
void* consumer(void* arg) {
pthread_t tid = pthread_self();
while (1) {
pthread_mutex_lock(&mutex);
while (count == 0) {
printf("[Consumer %lu] Buffer empty, waiting...\n", tid);
pthread_cond_wait(¬_empty, &mutex);
}
int item = buffer[--count];
printf("[Consumer %lu] Consumed %d (count=%d)\n", tid, item, count);
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&mutex);
sleep(2);
}
pthread_exit("Consumer finished");
}
// 可被取消的线程
void* cancellable(void* arg) {
pthread_t tid = pthread_self();
while (1) {
printf("[Cancellable %lu] Running...\n", tid);
sleep(1); // 取消点
}
return NULL;
}
// 分离线程
void* detached_routine(void* arg) {
pthread_t tid = pthread_self();
printf("[Detached %lu] Started (detached thread)\n", tid);
sleep(3);
printf("[Detached %lu] Finished automatically\n", tid);
return NULL;
}
int main() {
pthread_t prod, cons, cancel_th, detached;
void* retval;
// 1. 创建生产者线程
pthread_create(&prod, NULL, producer, NULL);
// 2. 创建消费者线程
pthread_create(&cons, NULL, consumer, NULL);
// 3. 创建可取消线程
pthread_create(&cancel_th, NULL, cancellable, NULL);
// 4. 创建分离线程
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&detached, &attr, detached_routine, NULL);
pthread_attr_destroy(&attr);
// 等待生产者线程退出
pthread_join(prod, &retval);
printf("[Main] Producer exited: %s\n", (char*)retval);
// 等待一会再取消 cancellable 线程
sleep(5);
printf("[Main] Cancelling cancellable thread...\n");
pthread_cancel(cancel_th);
pthread_join(cancel_th, &retval);
if (retval == PTHREAD_CANCELED) {
printf("[Main] Cancellable thread was canceled\n");
}
// 消费者线程是无限循环,这里演示强制退出
pthread_cancel(cons);
pthread_join(cons, &retval);
printf("[Main] Consumer thread ended\n");
printf("[Main] Program finished\n");
return 0;
}
运行结果:

看到不同线程的执行情况:
生产者不断产生数据
消费者取走数据
一个线程被取消
分离线程自动结束
四,线程池
1)线程池的工作机制:
1.在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
2.⼀个线程同时只能执行⼀个任务,但可以同时向⼀个线程池提交多个任务。
3.使用线程池的原因:
多线程运⾏时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这样就避免了繁琐的创建和结束线程的时间,有效的利⽤了CPU资源。
应用场景:有大量的数据处理请求,需要执行流并行或并发处理。
2)线程池实现

线程池示例程序:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
// 描述任务的结构(链表节点)
typedef struct CThread_worker {
void *(*process) (void *arg); // 任务函数指针
void *arg; // 任务函数参数
struct CThread_worker *next; // 指向下一个节点的指针
} CThread_worker;
// 描述线程池的结构
typedef struct {
pthread_mutex_t mutex; // 互斥锁,用于保护任务队列
pthread_cond_t cond; // 条件变量,用于线程的休眠和唤醒
CThread_worker *queue_head; // 任务队列的头指针
int shutdown; // 标志线程池是否销毁
pthread_t *threads; // 线程ID数组
int thread_count; // 线程数量
} CThread_pool;
// 全局线程池指针
CThread_pool *pool = NULL;
// 线程函数,负责阻塞线程,取任务和执行任务
void *thread_routine(void *arg) {
printf("线程 0x%lx 启动...\n", (unsigned long)pthread_self());
while (1) {
pthread_mutex_lock(&(pool->mutex));
// 如果任务队列为空并且线程池没有关闭,则线程休眠
while (pool->queue_head == NULL && !pool->shutdown) {
// pthread_cond_wait会原子地:1. 解锁mutex 2. 阻塞等待 3. 被唤醒后重新加锁
pthread_cond_wait(&(pool->cond), &(pool->mutex));
}
// 如果线程池被标记为销毁且任务队列已空,则退出线程
if (pool->shutdown && pool->queue_head == NULL) {
pthread_mutex_unlock(&(pool->mutex));
printf("线程 0x%lx 退出。\n", (unsigned long)pthread_self());
pthread_exit(NULL);
}
// 从任务队列头部取出一个任务
CThread_worker *worker = pool->queue_head;
pool->queue_head = worker->next;
pthread_mutex_unlock(&(pool->mutex));
// 执行任务
(worker->process)(worker->arg);
free(worker); // 释放已完成的任务节点
}
return NULL;
}
// 初始化线程池对象并给池子创建指定数量的线程
void pool_init(int max_thread_num) {
pool = (CThread_pool *) malloc(sizeof(CThread_pool));
pthread_mutex_init(&(pool->mutex), NULL);
pthread_cond_init(&(pool->cond), NULL);
pool->queue_head = NULL;
pool->thread_count = max_thread_num;
pool->shutdown = 0;
pool->threads = (pthread_t *) malloc(sizeof(pthread_t) * max_thread_num);
for (int i = 0; i < max_thread_num; i++) {
pthread_create(&(pool->threads[i]), NULL, thread_routine, NULL);
}
}
// 给线程池添加任务到链表中(尾插法)
int pool_add_worker(void *(*process) (void *arg), void *arg) {
// 创建新的任务节点
CThread_worker *new_worker = (CThread_worker *) malloc(sizeof(CThread_worker));
new_worker->process = process;
new_worker->arg = arg;
new_worker->next = NULL;
// 加锁保护任务队列
pthread_mutex_lock(&(pool->mutex));
// 将任务添加到队列尾部
CThread_worker *member = pool->queue_head;
if (member != NULL) {
while (member->next != NULL) {
member = member->next;
}
member->next = new_worker;
} else {
pool->queue_head = new_worker;
}
pthread_mutex_unlock(&(pool->mutex));
// 唤醒一个正在等待的线程
pthread_cond_signal(&(pool->cond));
return 0;
}
// 销毁线程池
int pool_destroy() {
if (pool->shutdown) {
return -1; // 防止重复销毁
}
// 1. 设置销毁标志位
pool->shutdown = 1;
// 2. 唤醒所有可能在休眠的线程,让他们自己退出
pthread_cond_broadcast(&(pool->cond));
// 3. 等待所有线程执行完毕并退出
for (int i = 0; i < pool->thread_count; i++) {
pthread_join(pool->threads[i], NULL);
}
// 4. 释放线程ID数组
free(pool->threads);
// 5. 销毁可能还残留在任务队列中的任务
CThread_worker *head = pool->queue_head;
while (head != NULL) {
CThread_worker *temp = head;
head = head->next;
free(temp);
}
// 6. 销毁互斥锁和条件变量
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->cond));
// 7. 释放线程池结构体
free(pool);
pool = NULL;
return 0;
}
// ---- 示例任务函数 ----
void* my_task(void* arg) {
int task_num = *(int*)arg;
printf("正在执行任务 %d (线程ID: 0x%lx)\n", task_num, (unsigned long)pthread_self());
sleep(1); // 模拟任务耗时
free(arg); // 释放传递进来的参数内存
return NULL;
}
// ---- 主函数 (测试) ----
int main() {
// 初始化一个包含4个线程的线程池
pool_init(4);
printf("线程池初始化完成。\n");
// 添加10个任务到线程池
for (int i = 0; i < 10; i++) {
int* task_num = (int*)malloc(sizeof(int));
*task_num = i + 1;
pool_add_worker(my_task, (void*)task_num);
printf("添加了任务 %d。\n", i + 1);
}
// 等待一段时间让所有任务都有机会被执行
printf("\n等待所有任务执行...\n");
sleep(5);
// 销毁线程池
printf("\n准备销毁线程池...\n");
pool_destroy();
printf("线程池已销毁。\n");
return 0;
}

836

被折叠的 条评论
为什么被折叠?



