目录
2.5 杀死(取消)线程 -- pthread_cancel
2.6 比较两个线程ID是否相等(预留函数) -- pthread_equal
1. 线程的概念
linux中,线程又叫做轻量级进程(light-weight process LWP),也有PCB,创建线程使用的底层函数和进程底层一样,都是clone,但没有独立的地址空间;而进程有独立地址空间,拥有PCB。
Linux下:线程是最小的执行单位,调度的基本单位。进程是最小分配资源单位,可看成是只有一个线程的进程。
线程是一个进程内部的控制序列。控制序列可以理解为一个执行流。进程内部是指虚拟地址空间。
主线程和子线程
共享:
- .text
- .bss
- .data
- 堆
- 动态加载区
- 环境变量
- 命令行参数
线程间通信:全局变量,堆
不共享: 若一共五个线程,栈区被平均分成五块
2. 线程相关函数
2.1 创建线程 ‐‐ pthread_create
int pthread_create( pthread_t *thread), //线程ID = 无符号长整型
const pthread_attr_t *attr, //线程属性,一般设置 NULL
void *(*start_routine)(void *), //线程处理函数
void *arg); //线程处理函数传入参数
参数:
pthread:传出参数,线程创建成功之后,会被设置一个合适的值
attr:默认传 NULL
start_routine:子线程的处理函数
arg: 回调函数的参数
返回值:
成功:0
错误:错误号 //perror不能使用该函数打印错误信息
主线程先退出,子线程会被强制结束
线程直接共享全局变量
2.2 单个线程退出 -- pthread_exit
函数原型: void pthread_exit(void *retval);
retval指针:必须指向全局,堆 一般填 NULL
// 在线程内调用 exit(0) 会退出整个进程,也就是其他线程也会退出
2.3 阻塞等待线程退出 -- pthread_join
函数原型:
int pthread_join(pthread_t pthread, void **retval)
参数:
pthread:要回收的子线程的ID
retval:读取线程退出的携带信息
传出参数
void* ptr;
pthread_join(pthid,&ptr);
指向的内存和pthread_exit参数指向地址一致
2.3.1代码举例 pthread_join.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int num = 100;
// myfunc 线程
void *myfunc(void *arg)
{
printf("child pthread id = %ld\n", pthread_self());
for (int i = 0; i < 5; i++)
{
printf("child pthread i = %d\n", i);
if (i == 2)
{
num = 666; // 验证不同线程可以利用全局变量通信
// pthread_exit(NULL); // 不携带数据的退出
pthread_exit(&num); // 携带数据的退出
}
}
return 0;
}
int main()
{
int ret;
int i = 0;
pthread_t pthid;
ret = pthread_create(&pthid, NULL, myfunc, NULL); // 线程创建
if (ret != 0) // 创建失败判断
{
printf("error number is %d\n", ret);
printf("%s\n", strerror(ret));
}
printf("parent pthread id = %ld\n", pthread_self());
void *ptr = NULL;
ptr = malloc(32); // 如果不动态申请内存会发生段错误
void *tmp = ptr; // 用 tmp 指向申请的内存来操作内存,以防改变 ptr 的指向导致 free 时产生段错误
pthread_join(pthid, &tmp); // 第二个参数是 void ** 类型的二级指针,可以用来获取 exit 参数携带的数据
printf("num = %d\n", *(int *)tmp);
free(ptr); // 释放掉申请的内存
ptr = NULL; // 指针指向 NULL 以防后续误操作
while (i < 5)
{
i++;
printf("parent pthread i = %d\n", i);
}
sleep(2);
return 0;
}
程序运行结果:
2.4 线程分离 -- pthread_detach
函数原型:int pthread_datach(pthread_t thread);
调用该函数之后不需要 pthread_join
子线程会自动回收自己的PCB
2.5 杀死(取消)线程 -- pthread_cancel
函数原型: int pthread_cancel(pthread_t pthread);
使用注意事项:
- 在要杀死的子线程对应的处理的函数的内部,必须做过一次系统调用
- 例如 write read printf
- int a = 2; int b = a+3; // 例如这样只做变量定义没有做系统调用则不能回收
pthread_testcancel(); 设置取消点
2.6 比较两个线程ID是否相等(预留函数) -- pthread_equal
函数原型:
int pthread_equal(pthread_t t1,pthread_t t2);
3. 线程的分离属性
通过属性设置线程的分离
线程属性类型:pthread_attr_t attr;
线程属性操作函数:
1. 对线程属性变量的初始化:
int pthread_attr_init(pthread_attr_t* attr);
2. 设置线程分离属性:
int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);
参数:
attr : 线程属性
detachstate:
PTHREAD_CREATE_DETACHED(分离)
PTHREAD_CREATE_JOINABLE(非分离)
释放线程资源函数
int pthread_attr_destroy(pthread_attr_t* attr);
4. 线程同步
多个线程对同一个共享资源进行操作的时候容易造成数据混乱。
解决方法:线程同步,使用锁。 就像上厕所一样,一个厕所只能一个人使用,想要使用厕所都需要把锁锁上防止别人进来,因此只有拿到锁才可以对共享资源进行操作,这样就保证了一个线程在操作共享资源的时候不会受到另一个线程的影响,避免了造成数据混乱。