目录
一、线程基础知识
1.进程特点
- 进程有独立的地址空间
- Linux为每个进程创建task_struct
- 每个进程都参与内核调度,互不影响
2.线程的引用
- 进程在切换时系统开销大
- 很多操作系统引入了轻量级进程LWP
- 同一进程中的线程共享相同地址空间
- Linux不区分进程、线程
3.线程特点
通常线程指的是共享相同地址空间的多个任务
使用多线程的好处:
- 大大提高了任务切换的效率
- 避免了额外的TLB & cache的刷新
4.线程资源
(1)线程共享资源
一个进程中的多个线程共享以下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前工作目录
- 用户ID
- 用户组ID
(2)线程私有资源
每个线程私有的资源包括:
- 线程ID (TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号 (errno)
- 优先级
- 执行状态和属性
5.Linux线程库
pthread线程库中提供了如下基本操作:
- 创建线程
- 回收线程
- 结束线程
同步和互斥机制:
- 信号量
- 互斥锁
二、线程相关命令
1.线程创建 – pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);
参数:
成功返回0,失败时返回错误码
thread——线程对象
attr ——线程属性,NULL代表默认属性
routine ——线程执行的函数
arg ——传递给routine的参数 ,参数是void * ,注意传递参数格式
示例代码:创建一个简单的线程。如果不加睡眠时间,可能导致主函数已经退出,而线程还没有创建和执行结束,这样线程里的内容就不会被执行
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
int *testThread(char *arg)//线程执行函数
{
printf("This is a thread test\n");//调用线程打印
return NULL;
}
int main()
{
pthread_t tid;
int ret;
ret = pthread_create(&tid,NULL,(void *)testThread,NULL);
printf("This is main thread\n");
sleep(1);//睡眠1s是为了让线程创建和执行结束
}
注:编译时候加 -lpthread(动态链接库),否则会出现链接错误。
2.线程结束 – pthread_exit
#include <pthread.h>
void pthread_exit(void *retval);
要点:
- 结束当前线程
- retval 可被其他线程通过pthread_join获取 (后续线程的回收有讲解)
- 线程私有资源被释放
3.获取线程的id
(1)通过pthread_create函数的第一个参数
(2)通过在线程里面调用pthread_self函数
#include <pthread.h>
pthread_t pthread_self(void);//再线程中调用,可以查看自己的TID
注:使用printf打印时,使用“%lu”打印(tid)pthread_t类型的值
4.线程间参数传递
线程创建时:
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);//arg ——传递给routine的参数 ,参数是void * ,注意传递参数格式
线程执行函数(routine):
void * testThread(void * arg)
示例代码:通过取地址将int型的数值传给线程执行函数,注意里面arg的强制类型转换
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("input arg=%d\n",*(int *)arg);
pthread_exit(NULL);
}
int main()
{
pthread_t tid;
int arg = 5;
pthread_create(&tid,NULL,testThread,(void *)&arg); //将arg的值传给线程执行函数
sleep(1);
}
特点:效率低(如果不加睡眠时间可能导致主程序运行完,地址仍没传完)、不明朗,但不会有编译警告
示例代码:将int型的数值当成地址直接传给线程执行函数
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *testThread(void *arg)
{
printf("input arg=%d\n",(int)arg);
pthread_exit(NULL);
}
int main()
{
pthread_t tid;
int arg = 5;
pthread_create(&tid,NULL,testThread,(void *)arg);
sleep(1);
}
特点:效率高,但是存在字节长度不够而数据丢失的风险,编译器会告警,需要程序员自己保证数据长度正确
5.线程回收 – pthread_join
对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
参数:
- 成功返回0,失败时返回错误码
- thread——要回收的线程对象
- 调用线程阻塞直到thread结束
- *retval——接收线程thread的返回值
示例代码:编写一个多线程程序,实现线程的回收(100个线程)
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg)
{
printf("This is child thread\n");
sleep(25);
pthread_exit("thread return");
}
int main()
{
pthread_t tid[100];
void *retv;
int i;
for(i = 0;i < 100;i++)
{
pthread_create(&tid[i],NULL,func,NULL);
}
for(i=0;i<100;i++)
{
pthread_join(tid[i],&retv);//线程回收
}
while(1)
{
sleep(1);
}
}
线程回收演示:
ps -eLf 线程查看命令
top 命令查看内存:

回收前:

回收后:

特点:
pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待;故当建立多个线程时,若前面其中一个线程没有结束,则后续的线程就全部回收不了。
解决方法:被线程从进程中分离出来
6.线程分离
线程主动与主控线程断开关系,线程结束后不会产生僵尸线程(这样就不需要回收线程)。
方式一:在线程执行函数中调用pthead_detach函数
int pthread_detach(pthread_t thread);
成功:0;失败:错误号
方式二:通过线程属性来设置游离态(分离态)
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
本文探讨了线程的基础知识,如进程与线程的区别、线程资源的共享与私有性,以及Linux线程库中的关键命令,如线程创建、结束、回收和参数传递。通过实例展示了如何利用pthread_create和pthread_join进行高效编程实践。

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



