目录
1、概念
共享的资源:
进程内存空间:同一进程中的所有线程共享该进程的内存,包括堆区和数据段(全局变量、静态变量等)。不同线程可以访问和修改共享的全局变量和动态分配的内存。
文件描述符:所有线程共享进程打开的文件描述符,这意味着它们可以共享文件、网络连接等资源。
进程的代码段:所有线程执行相同的程序代码。
内存映射区域(Memory-mapped regions):如果有内存映射的文件或共享内存区域,线程也可以共享这些内存区域。
独立的资源:
线程栈:每个线程都有自己的栈空间,用于存储局部变量和函数调用信息。不同线程的栈是相互独立的,互不干扰。
程序计数器(PC):每个线程有自己独立的程序计数器,用来跟踪该线程当前执行到哪一条指令。
寄存器:每个线程有自己的寄存器值,独立于其他线程。
线程局部存储(TLS):每个线程可以有自己的线程局部存储(例如,
pthread
中的thread_local
变量),这些变量对其他线程是不可见的。
健壮性:进程健壮性更强,同一进程下的多线程,有一个线程崩溃整个进程就会崩溃。
切换效率:不同进程之间的切换效率低于同一进程下的多线程之间切换。
2、线程API函数
2.1创建线程

-l线程库名
练习
创建线程
#include "stdio.h"
#include <pthread.h>
void * Func (void * arg)
{
while(1)
{
printf("新创建线程打印输出\n");
sleep(1);
}
}
int main(int argc, char const *argv[])
{
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
pthread_t My_Pth_TID = 1;
pthread_create(&My_Pth_TID,NULL,Func,NULL);
while(1)
{
printf("这里是初始线程打印输出\n");
sleep(1);
}
return 0;
}
2.2线程属性
1. 线程创建类型 (
pthread_create
的行为)
属性类型:
PTHREAD_CREATE_JOINABLE
或PTHREAD_CREATE_DETACHED
说明:此属性指定线程的创建方式。
PTHREAD_CREATE_JOINABLE
:这是默认的设置,表示线程在结束后可以被pthread_join()
等待。
PTHREAD_CREATE_DETACHED
:表示线程是分离的,线程结束后系统自动回收其资源,不能被pthread_join()
等待。2. 线程栈大小 (
pthread_attr_setstacksize
)
属性类型:
size_t
说明:设置线程的栈大小。每个线程都有一个栈,它是线程执行时的内存区域。如果栈空间不足,线程可能会崩溃或出现栈溢出。
函数:
pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
例如:
pthread_attr_setstacksize(&attr, 1024 * 1024);
设置线程栈大小为 1MB。3. 线程栈地址 (
pthread_attr_setstack
)
属性类型:
void *
说明:设置线程栈的起始地址。这通常用于在特定的内存区域分配栈。一般情况下不需要设置,除非你有特殊需求(比如在嵌入式系统中)。
函数:
pthread_attr_setstack(pthread_attr_t *attr, void *stack, size_t stacksize)
例如:
pthread_attr_setstack(&attr, stack_buffer, 1024 * 1024);
设置栈起始地址为stack_buffer
。4. 线程调度策略 (
pthread_attr_setschedpolicy
)
属性类型:
int
说明:设置线程的调度策略。不同的调度策略决定了操作系统如何安排线程执行。
SCHED_FIFO
:先来先服务调度。
SCHED_RR
:轮转调度。
SCHED_OTHER
:默认调度策略,通常是基于时间片的调度策略。函数:
pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
例如:
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
设置为先来先服务调度。5. 线程优先级 (
pthread_attr_setschedparam
)
属性类型:
struct sched_param
说明:设置线程的调度优先级。优先级值通常是整数,值越大,线程的优先级越高。
函数:
pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param)
例如:
struct sched_param param; param.sched_priority = 20; pthread_attr_setschedparam(&attr, ¶m);
设置线程的优先级为 20。6. 线程继承调度策略 (
pthread_attr_setinheritsched
)
属性类型:
int
说明:设置线程是否继承创建线程的调度属性。
PTHREAD_INHERIT_SCHED
:线程继承创建线程的调度策略和优先级。
PTHREAD_EXPLICIT_SCHED
:线程不继承父线程的调度属性,使用自己设置的属性。函数:
pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched)
例如:
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
设置线程不继承父线程的调度策略。7. 线程的守护线程属性 (
pthread_attr_setdetachstate
)
属性类型:
int
说明:设置线程的分离状态。
PTHREAD_CREATE_DETACHED
:线程是分离的,不能被pthread_join()
等待。
PTHREAD_CREATE_JOINABLE
:线程是可连接的,允许pthread_join()
等待线程结束。函数:
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
例如:
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
设置线程为分离状态。8. 线程的调度优先级范围 (
pthread_attr_getschedparam
和pthread_attr_getschedpolicy
)
说明:用于获取线程的调度参数和调度策略
获取线程属性API:

设置线程属性API
主要的几个属性API
获取、设置线程的分离属性

以下接口跟线程的调度相关:
获取、设置线程是否继承创建者的调度策略
当需要给一个线程设置调度方面的属性时,必须先将线程的inheritsched设置为PTHREAD_EXPLICIT_SCHED。
获取、设置线程的调度策略
线程的静态优先级和动态优先级的设置
跟线程的栈和警戒区的大小相关API
获取、设置线程栈大小、警戒区大小
为什么常常不需要增大栈的空间?
- 默认的栈大小通常已经足够。
- 栈溢出问题常常是由于算法设计或代码逻辑问题,而不是栈空间不足。
- 增加栈空间会浪费系统资源,并不一定能解决根本问题。 因此,最好的做法是通过优化代码和算法来避免栈溢出。
退出、接合线程
)。下面是相关 API:
retval
的取值逻辑:
线程被取消(通过
pthread_cancel
):
retval
会被设置为PTHREAD_CANCELED
(一个特殊宏,通常是(void*)-1
)。线程正常退出(通过
return
或pthread_exit
):
retval
的值是线程函数的返回值(或pthread_exit
传入的指针)。线程未终止(未调用
pthread_join
前):
调用pthread_join
的线程会阻塞,直到目标线程结束。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
void * Func (void * arg)
{
while(1)
{
printf("新创建线程打印输出\n");
sleep(3);
break;
}
//void *calloc(size_t nmemb, size_t size);
//注意这里不能创建一个变量进行返回,因为当这个线程退出后,创建变量的源空间就会销毁。
//因此我们需要再堆上开辟空间返回!!!!
int* tetVal_Addr = (int*)calloc(1, 4);
*tetVal_Addr = 115200;
printf("新线程准备退出\n");
//void pthread_exit(void *retval);
pthread_exit((void*)tetVal_Addr);//之后的程序不会被执行
printf("新线程已退出\n");
}
int main(int argc, char const *argv[])
{
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
pthread_t My_Pth_TID = 1;
pthread_create(&My_Pth_TID,NULL,Func,NULL);
while(1)
{
printf("这里是初始线程打印输出\n");
sleep(1);
break;
}
//接合线程,回收线程资源
//阻塞等待接合
//int pthread_join(pthread_t thread, void **retval);
int *retval ;
pthread_join(My_Pth_TID, (void*)&retval);
printf("新线程的退出返回值为:%d\n",*retval);
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
void * Func(void *arg)
{
while (1)
{
printf("新创建线程打印输出\n");
sleep(3);
break;
}
// 直接打印数值,而不是返回指针
int tetVal = 115200;
printf("新线程准备退出,返回值为:%d\n", tetVal);
// 退出线程
pthread_exit(NULL); // 无需返回指针
}
int main(int argc, char const *argv[])
{
pthread_attr_t attr;
// 初始化线程属性
pthread_attr_init(&attr);
// 将线程设置为分离属性,(线程在退出后可以直接被系统收尸)
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 将线程设置为接合属性
//pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
pthread_t My_Pth_TID;
pthread_create(&My_Pth_TID, &attr, Func, NULL);
// 销毁线程属性
pthread_attr_destroy(&attr);
while (1)
{
printf("这里是初始线程打印输出\n");
sleep(10);
break;
}
//分离线程不需要 pthread_join,移除以下代码
int *retval ;
if (0 == pthread_join(My_Pth_TID, (void*)&retval))
{
printf("新线程的退出接合成功,返回值为:%d\n",*retval);
}
else
{
printf("接合失败\n");
}
return 0;
}
给指定线程发送一个取消请求(杀死线程)

练习:杀掉创建的线程
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
void * Func (void * arg)
{
while(1)
{
printf("新创建线程打印输出\n");
sleep(3);
}
//void *calloc(size_t nmemb, size_t size);
int* tetVal_Addr = (int*)calloc(1, 4);
*tetVal_Addr = 115200;
printf("新线程准备退出\n");
//void pthread_exit(void *retval);
pthread_exit((void*)tetVal_Addr);//之后的程序不会被执行
printf("新线程已退出\n");
}
int main(int argc, char const *argv[])
{
int i = 0;
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
pthread_t My_Pth_TID = 1;
pthread_create(&My_Pth_TID,NULL,Func,NULL);
while(1)
{
printf("这里是初始线程打印输出\n");
sleep(1);
i ++;
if(i > 10)
{
printf("我要杀掉创建的进程\n");
//杀死创建的进程
pthread_cancel(My_Pth_TID);
printf("我要杀掉完成\n");
sleep(5);
break;
}
}
//如果杀死线程,就不能进行接合操作!!!!!
//接合线程,回收线程资源
//阻塞等待接合
//int pthread_join(pthread_t thread, void **retval);
// int *retval ;
// pthread_join(My_Pth_TID, (void*)&retval);
// printf("新线程的退出返回值为:%d\n",*retval);
return 0;
}
通过结果我们可知:线程被杀掉我们不能去接合突然死掉的线程。
获取、设置线程的取消状态和取消类型
测试:
启用取消+异步取消
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
// 线程2:启用取消 + 异步取消类型
void* thread_func2(void* arg) {
int old_state, old_type;
// 获取并设置取消状态(虽然默认是启用,这里为了演示API使用)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state);
// 设置取消类型为异步(PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
printf("Thread2: 旧取消类型: %s\n",
(old_type == PTHREAD_CANCEL_DEFERRED) ? "延迟" : "异步");
// 模拟工作循环(没有取消点)
while(1) {
printf("Thread2: 运行中...\n");
sleep(1);
// 注意:异步取消不需要取消点
// 这里故意不使用sleep等可能产生取消点的函数
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
// 创建线程2(异步取消)
if(pthread_create(&tid2, NULL, thread_func2, NULL) != 0) {
perror("创建线程2失败");
return 1;
}
sleep(2); // 等待线程运行
// 发送取消请求
printf("\n主线程发送取消请求...\n");
pthread_cancel(tid2);
// 等待线程结束
void* ret1, *ret2;
pthread_join(tid2, &ret2);
printf("线程2退出状态: %s\n",
(ret2 == PTHREAD_CANCELED) ? "被取消" : "正常退出");
return 0;
}
2.禁用取消+延时取消
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
// 线程1:禁用取消请求 + 延迟取消(默认类型,虽然实际不会执行取消)
void* thread_func1(void* arg) {
int old_state;
// 设置取消状态为禁用(PTHREAD_CANCEL_DISABLE)
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
printf("Thread1: 旧取消状态: %s\n",
(old_state == PTHREAD_CANCEL_ENABLE) ? "启用" : "禁用");
// 模拟工作循环
for(int i = 0; i < 5; i++) {
printf("Thread1: 工作中...(%d/5)\n", i+1);
sleep(1);
}
// 重新启用取消
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state);
printf("Thread1: 完成工作,退出\n");
return NULL;
}
// 线程2:启用取消 + 异步取消类型
void* thread_func2(void* arg) {
int old_state, old_type;
// 获取并设置取消状态(虽然默认是启用,这里为了演示API使用)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state);
// 设置取消类型为异步(PTHREAD_CANCEL_ASYNCHRONOUS)
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type);
printf("Thread2: 旧取消类型: %s\n",
(old_type == PTHREAD_CANCEL_DEFERRED) ? "延迟" : "异步");
// 模拟工作循环(没有取消点)
while(1) {
printf("Thread2: 运行中...\n");
sleep(1);
// 注意:异步取消不需要取消点
// 这里故意不使用sleep等可能产生取消点的函数
}
return NULL;
}
int main() {
pthread_t tid1, tid2;
// 创建线程1(禁用取消)
if(pthread_create(&tid1, NULL, thread_func1, NULL) != 0) {
perror("创建线程1失败");
return 1;
}
// // 创建线程2(异步取消)
// if(pthread_create(&tid2, NULL, thread_func2, NULL) != 0) {
// perror("创建线程2失败");
// return 1;
// }
sleep(2); // 等待线程运行
// 发送取消请求
printf("\n主线程发送取消请求...\n");
pthread_cancel(tid1);
// pthread_cancel(tid2);
// 等待线程结束
void* ret1, *ret2;
pthread_join(tid1, &ret1);
// pthread_join(tid2, &ret2);
printf("\n线程1退出状态: %s\n",
(ret1 == PTHREAD_CANCELED) ? "被取消" : "正常退出");
// printf("线程2退出状态: %s\n",
// (ret2 == PTHREAD_CANCELED) ? "被取消" : "正常退出");
return 0;
压栈、或弹栈线程的取消处理例程
练习:
1. 一个线程函数,使用pthread_cleanup_push注册清理函数。
2. 清理函数的具体实现,如关闭文件或释放内存。
3. 主线程创建子线程,并在适当时候取消它。
4. 使用pthread_cleanup_pop弹出清理处理程序,可能带参数执行或不执行。
5. 验证资源是否被正确释放,无论线程是被取消还是正常退出。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
/* 模拟资源结构体 */
typedef struct {
FILE* logfile;
char* buffer;
} ThreadResource;
// 清理函数1:关闭文件
void cleanup_file(void* arg) {
FILE** fp = (FILE**)arg;
if (*fp) {
fclose(*fp);
printf("清理处理: 已关闭文件\n");
*fp = NULL;
}
}
// 清理函数2:释放内存
void cleanup_memory(void* arg) {
char** buffer = (char**)arg;
if (*buffer) {
free(*buffer);
printf("清理处理: 已释放内存\n");
*buffer = NULL;
}
}
void* thread_function(void* arg) {
ThreadResource res = {NULL, NULL};
/* 压栈清理处理程序(FILO顺序) */
pthread_cleanup_push(cleanup_file, &res.logfile);
pthread_cleanup_push(cleanup_memory, &res.buffer);
/* 关键代码段开始 */
printf("线程: 正在打开日志文件\n");
res.logfile = fopen("thread_log.txt", "w");
if (!res.logfile) {
perror("fopen失败");
pthread_exit(NULL);
}
printf("线程: 正在分配缓冲区\n");
res.buffer = (char*)malloc(1024);
if (!res.buffer) {
perror("malloc失败");
pthread_exit(NULL);
}
/* 模拟耗时操作(可能被取消)*/
for (int i = 0; i < 5; i++) {
printf("线程: 正在工作(%d/5)\n", i+1);
sleep(1); // 这是一个取消点
}
/* 关键代码段结束 */
/* 弹栈清理处理程序(0表示不执行)*/
pthread_cleanup_pop(0); // 弹出内存清理但不执行
pthread_cleanup_pop(0); // 弹出文件清理但不执行
/* 正常释放资源 */
free(res.buffer);
fclose(res.logfile);
printf("线程: 正常完成资源释放\n");
return NULL;
}
int main() {
pthread_t tid;
ThreadResource resource = {NULL, NULL};
if (pthread_create(&tid, NULL, thread_function, &resource) != 0) {
perror("pthread_create失败");
return 1;
}
sleep(2); // 等待线程进入工作状态
printf("\n主线程: 发送取消请求\n");
pthread_cancel(tid);
void* retval;
pthread_join(tid, &retval);
if (retval == PTHREAD_CANCELED) {
printf("\n线程已被成功取消\n");
} else {
printf("\n线程正常退出\n");
}
return 0;
}