当前位置: 首页 > article >正文

Linux----线程

一、基础概念对比

特性进程 (Process)线程 (Thread)
资源分配资源分配的基本单位(独立地址空间)共享进程资源
调度单位操作系统调度单位CPU调度的最小单位
创建开销高(需复制父进程资源)低(共享进程资源)
通信方式管道、共享内存、消息队列等IPC共享全局变量(需同步机制)
隔离性内存隔离,安全性高共享内存,需处理竞争条件
典型组成代码段+数据段+堆栈段+PCB线程ID+寄存器组+栈+线程控制块TCB

二、线程组成详解

1. 核心组件

struct thread_struct {
    pthread_t tid;           // 线程ID (8字节)
    void* stack_base;        // 栈基地址 (8字节)
    size_t stack_size;       // 栈大小 (Linux默认8MB)
    void* (*start_routine)(void*); // 入口函数指针
    void* arg;               // 入口函数参数
    // 寄存器组保存区 (约52个寄存器,约416字节)
    // 包括:PC、SP、通用寄存器、浮点寄存器等
};

2. 关键特征

  • 线程IDpthread_t 类型,进程内唯一
  • 独立栈空间:每个线程拥有独立调用栈
  • 共享资源:全局变量、堆内存、文件描述符等

三、线程创建与管理

1. 创建函数原型

#include <pthread.h>
int pthread_create(pthread_t *thread, 
                   const pthread_attr_t *attr,
                   void *(*start_routine)(void *), 
                   void *arg);
参数详解表
参数类型作用说明
threadpthread_t*输出参数,存储新线程ID
attrpthread_attr_t*线程属性(NULL使用默认属性):<br>▪ 栈大小<br>▪ 调度策略<br>▪ 分离状态
start_routinevoid* (*)(void*)线程入口函数(返回值为线程退出状态)
argvoid*传递给入口函数的参数
返回值
  • 成功返回0
  • 失败返回错误码(非errno值,需用strerror转换)

2. 编译指令

gcc program.c -lpthread -o program  # 必须链接pthread库

3. 线程终止方式

/* 主动退出(带返回值)*/
void pthread_exit(void *retval);

/* 被动终止(被其他线程取消)*/
int pthread_cancel(pthread_t thread);
注意事项
  • retval必须指向堆/静态存储区,不能是线程栈内存
  • 主线程退出会导致进程终止(即使其他线程仍在运行)

四、线程终止方式详解(补充)

根据POSIX标准,线程可通过以下四种方式终止执行:

1. 显式调用退出函数

void* worker(void* arg) {
    // 动态分配返回值
    int* result = malloc(sizeof(int));
    *result = 100;
    
    // 显式退出并传递状态值
    pthread_exit((void*)result);  // 正确:堆内存
    // pthread_exit(&local_var);  // 危险!栈内存会被回收
}

特点

  • 退出状态值通过pthread_join()获取
  • 必须保证返回值内存有效性(推荐使用堆内存或全局变量)

2. 从入口函数返回

void* worker(void* arg) {
    static int result = 200;  // 静态存储期变量
    return (void*)&result;    // 等效于 pthread_exit()
}

注意

  • 返回值类型必须为void*
  • 禁止返回局部变量地址(函数退出后栈空间失效)

3. 被其他线程取消

// 请求取消目标线程
pthread_cancel(tid);

// 目标线程中设置取消点
void* worker(void* arg) {
    while(1) {
        pthread_testcancel();  // 手动设置取消点
        // 长时间工作...
    }
    return NULL;
}

关键机制

取消类型行为特征设置函数
PTHREAD_CANCEL_DEFERRED(默认)在下一个取消点终止pthread_setcanceltype()
PTHREAD_CANCEL_ASYNCHRONOUS立即终止(可能破坏数据一致性)pthread_setcanceltype()

4. 进程级终止

void* thread_func(void* arg) {
    sleep(1);
    printf("此消息不会被打印\n");
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_create(&tid, NULL, thread_func, NULL);
    
    // 错误示范:主线程直接返回
    // return 0;  // 导致所有线程立即终止
    
    // 正确做法:主线程等待子线程
    pthread_exit(NULL);  // 仅退出主线程,不影响其他线程
}

重要规则

  • exit()会终止整个进程及其所有线程
  • 主线程return会隐式调用exit()
  • 建议主线程使用pthread_exit()代替return

五、线程状态回收机制

1. 等待线程终止

void* status;
int ret = pthread_join(tid, &status);

if (ret == 0) {
    printf("线程退出码:%d\n", *(int*)status);
    free(status);  // 清理堆内存
} else {
    perror("等待线程失败");
}

2. 分离线程(自动回收)

// 创建时设置分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, worker, NULL);

// 或运行时分离
pthread_detach(tid);
//注意在建立线程后就设置分离

特性

  • 分离线程终止后自动回收资源
  • 无法使用pthread_join()获取状态
  • 适用于不需要返回值的后台任务

六、线程终止流程图解

graph TD
    A[线程开始] --> B{终止方式}
    B -->|pthread_exit| C[传递状态值]
    B -->|return| C
    B -->|pthread_cancel| D[清理处理程序]
    B -->|exit| E[终止所有线程]
    
    C --> F[状态值存储]
    D --> G[调用清理栈函数]
    
    F --> H[pthread_join获取]
    G --> I[资源释放]
    
    E --> J[进程终止]
    
    style C fill:#c9f,stroke:#333
    style D fill:#f96,stroke:#333
    style E fill:#f00,stroke:#333

七、最佳实践建议

  1. 资源管理三原则

    • 谁分配谁释放
    • 退出前释放非共享资源
    • 使用pthread_cleanup_push()注册清理函数
  2. 取消安全设计

    void cleanup(void* arg) {
        printf("清理资源:%p\n", arg);
        free(arg);
    }
    
    void* worker(void* arg) {
        void* res = malloc(1024);
        pthread_cleanup_push(cleanup, res);
        
        // 可能被取消的代码区
        while(1) {
            pthread_testcancel();
            // 关键操作...
        }
        
        pthread_cleanup_pop(1);  // 执行清理
        return NULL;
    }
    
  3. 状态值传递规范

    • 简单状态码使用int类型转换
      pthread_exit((void*)(intptr_t)error_code);
      
    • 复杂数据结构使用堆内存
      struct Result* res = malloc(sizeof(struct Result));
      /* 填充数据 */
      pthread_exit(res);

4.练习

    练习1:创建一个线程
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>

void * do_something(void *arg)
{
	printf("do copy file---\n");
	return NULL;
}

int main(int argc, const char *argv[])
{
	pthread_t tid;
	int ret;
	if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0)
	{
		errno = ret;
		perror("pthread_create fail");
		return -1;
	}

	printf("-----main-------\n");
	sleep(1);
	return 0;
	
	
	return 0;
}
  练习2:创建多个线程
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>
 
void * do_one(void *arg)
{
	printf("pthread 1 pid = %d\n",getpid());
	return NULL;
}

void * do_two(void *arg)
{
	printf("pthread 2 pid = %d\n",getpid());
	return NULL;
}

void * do_three(void *arg)
{
	printf("pthread 3 pid = %d\n",getpid());
	return NULL;
}

typedef void *(*thread_cb_t)(void*);

int main(int argc, const char *argv[])
{
	printf("---main---  pid = %d\n",getpid());
	
	
	pthread_t tid[3];
	int ret;
	thread_cb_t func[3] = {do_one,do_two,do_three};

	int i = 0;
	for(i = 0;i < 3;i++)
	{
		if((ret = pthread_create(&tid[i],NULL,func[i],NULL)) != 0)
		{
			errno = ret;
			perror("pthread1_create fail");
			return -1;
		}
	}
		sleep(1);
	return 0;

	
	return 0;
}
 练习3:线程的关闭
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>

void * do_something(void *arg)
{
	static int ret = 100;
	printf("do copy file---\n");

	//pthread_exit("i am dead\n");
	pthread_exit(&ret);

	//return NULL;
}

int main(int argc, const char *argv[])
{
	pthread_t tid;
	int ret;
	if((ret = pthread_create(&tid,NULL,do_something,NULL)) != 0)
	{
		errno = ret;
		perror("pthread_create fail");
		return -1;
	}

	printf("-----main-------\n");


	int *retval;
	//char *retval;
	pthread_join(tid,(void **)&retval);

	//printf("*retval = %s\n",retval);
	printf("*retval = %d\n",*retval);
	sleep(1);
	return 0;
	
	
	return 0;
}

练习4:多线程拷贝文件(缺陷当文件过大,会导致偏移量出错)

#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>


typedef struct
{
	int fd_s;
	int fd_d;
	int size;
	int len;
	int id;
}msg_t;


void * do_copy (void *arg)
{
	msg_t p = *(msg_t*)arg;

	lseek(p.fd_s,p.size*p.id,SEEK_SET);
	lseek(p.fd_d,p.size*p.id,SEEK_SET);

//	printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p.id,p.fd_s,p.fd_d,p.size,p.len);//调试代码

	char buf[p.len];
	int ret = read(p.fd_s,buf,p.len);
	write(p.fd_d,buf,ret);

	return NULL; 
}

//cp src dest 
int main(int argc, const char *argv[])
{
	if (argc!=3)
	{
		printf("Usage: %s <src> <dest>\n",argv[0]);
		return -1;
	}
	
	int fd_s = open(argv[1],O_RDONLY);
	int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666);
	if (fd_s < 0 || fd_d < 0)
	{
		perror("open fail");
		return -1;
	}

	int n = 0;
	printf("Input threads num: ");
	scanf("%d",&n);

	int i = 0;
	int ret = 0;
	pthread_t tid[n];
	
	msg_t msg[n];
	struct stat st;

	if (stat(argv[1],&st) < 0)
	{
		perror("stat fail");
		return -1;
	}
	int f_len = st.st_size;

	
	for (i = 0; i < n; ++i)
	{
		msg[i].fd_s = fd_s;
		msg[i].fd_d = fd_d;
		msg[i].size = f_len / n;
		msg[i].id = i;

	  
#if 1
		if (i == n-1)
		{ 
			msg[i].len  = f_len - (f_len/n)*(n-1);
		}else 
		{
			msg[i].len  = f_len/n;
		}
#endif
		ret = pthread_create(&tid[i],NULL,do_copy,&msg[i]);

		if (ret != 0)
		{
			errno = ret;
			perror("pthread_create fail");
			return -1;
		}

	}


	printf("----main-----\n");
	for (i = 0; i < n; ++i)
		pthread_join(tid[i],NULL);

	close(fd_s);
	close(fd_d);
	
	return 0;
}


八、线程生命周期管理

1. 线程属性设置(示例)

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 分离属性
pthread_attr_setstacksize(&attr, 2*1024*1024);               // 设置2MB栈

2. 线程同步机制

机制用途相关函数
互斥锁保护共享资源pthread_mutex_*系列
条件变量线程间事件通知pthread_cond_*系列
读写锁读写操作分离pthread_rwlock_*系列
信号量控制并发访问数量sem_*系列

九、典型问题与解决方案

1. 资源竞争问题

场景:多个线程同时修改全局变量
解决

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* counter_thread(void* arg) {
    for(int i=0; i<100000; ++i) {
        pthread_mutex_lock(&mutex);
        global_counter++;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

2. 僵尸线程问题

现象:已终止但未回收的线程占用系统资源
解决方案

  • 使用pthread_join阻塞回收:
    void* retval;
    pthread_join(tid, &retval);  // 类似进程的waitpid
    free(retval);                // 清理返回值
    
  • 或设置分离属性:
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

http://www.kler.cn/a/556085.html

相关文章:

  • SpringBoot核心框架之AOP详解
  • 拆解微软CEO纳德拉战略蓝图:AI、量子计算、游戏革命如何改写未来规则!
  • 【WRF模拟】全过程总结:更换不同研究时段改动总结
  • 宝塔mysql8.0设置数据库区分大小写
  • 宝塔怎么搭建LiveHelperChat?PHP开源在线客服安装教程
  • Lua C API :lua_insert 函数详解
  • WPS携手DeepSeek:开启智能办公新时代
  • Linux基础开发工具的使用(apt、vim、gcc、g++、gdb、make、makefile)
  • 计算机网络面试知识点总结
  • 开源免费文档翻译工具 可支持pdf、word、excel、ppt
  • java练习(36)
  • Qt中使用QPdfWriter类结合QPainter类绘制并输出PDF文件
  • 15增减字符串匹配(贪心)思路解析+源码
  • WPS接入deepseek-OfficeAI助手插件下载
  • 武汉火影数字|VR沉浸式空间制作 VR大空间打造
  • Spring面试题2
  • uniapp引入ucharts组件不显示
  • Flask flash() 消息示例
  • ChatGPT付费创作系统V3.1.3独立版 WEB端+H5端+小程序端 (DeepSeek高级通道+推理输出格式)安装教程
  • 在windows下安装windows+Ubuntu16.04双系统(上)