1.线程的概念
线程依赖于进程而存在,线程运行所需的系统资源全部都是进程给它的
线程和进程都是为了实现多任务的并发执行
主线程:创建线程的那个主函数,叫做主线程
子线程:创建出来的线程,叫做子线程
2.linux提供线程有关的接口函数
(1)创建子线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:thread --》存放线程ID号
attr --》存放线程属性,单独讨论,暂时不用理会
void *(*start_routine) (void *) --》函数指针,线程的任务函数(你创建线程需要完成的任务就靠它来实现)
arg --》传递给第三个参数的
Compile and link with -pthread. //使用多线程编译的时候需要链接线程库
(2)线程的退出
void pthread_exit(void *retval);
参数:retval --》保存线程退出的信息,void *表明可以返回任何变量的地址
线程的回收
int pthread_join(pthread_t thread, void **retval); 晕针
参数:thread --》线程的ID号
retval --》二级指针,存放线程退出时候返回的信息
pthread_join会阻塞主函数,等待子线程退出,如果你的子线程一直不退出,该函数会一直阻塞
(3)线程的取消 --》干掉线程
int pthread_cancel(pthread_t thread);
参数:thread --》线程的ID号
设置线程的取消状态
有两种状态:可以取消和不可以取消
默认情况下线程是可以取消的
int pthread_setcancelstate(int state, int *oldstate);
参数: state --》PTHREAD_CANCEL_ENABLE 能取消
PTHREAD_CANCEL_DISABLE 不能取消
oldstate --》备份线程原本的取消状态
设置线程的取消类型
前提条件是线程是能够被取消的
取消类型有两种:立即取消和延时取消
立即取消: 调用pthread_cancel函数成功,子线程立马挂掉
延时取消: 调用pthread_cancel函数成功,子线程不会立马退出,会继续往后执行,直到遇到取消点函数才结束
取消点函数:man 7 pthreads 查看所有的取消点函数
int pthread_setcanceltype(int type, int *oldtype);
参数:type --》PTHREAD_CANCEL_DEFERRED 延时取消
PTHREAD_CANCEL_ASYNCHRONOUS 立即取消
oldtype --》备份线程原本的取消类型
(4)线程取消时候要调用执行的例程函数
push()和pop()必须成对使用
原理和作用: 但一个线程被取消的时候,push的第一个参数指向的函数会被自动调用
void pthread_cleanup_push(void (*routine)(void *), void *arg);
参数:void (*routine)(void *) --》线程被取消的时候,需要执行的例程函数
arg --》传递给routine的参数
void pthread_cleanup_pop(int execute);
参数:execute --》 0 出栈,但不执行routine指向的函数
非0 出栈,执行routine指向的函数
总结:如果你不想线程被干掉(取消)
方法一:pthread_setcancelstate()把线程设置成不可以被取消
方法二:push和pop配合,但线程被取消的时候,去执行push第一个参数指向的函数,接着完成其它任务
线程的创建
#include "myhead.h"
//自定义函数来表示线程的任务函数
void *fun(void *arg)
{
//代表的就是子线程的代码
while(1)
{
printf("我是子线程,我帮你杀毒!\n");
sleep(1);
}
}
int main() //主线程
{
pthread_t id;
//创建子线程--》帮助我杀毒
pthread_create(&id,NULL,fun,NULL);
//我自己--》清除垃圾
while(1)
{
printf("我自己清除垃圾!\n");
sleep(1);
}
}
线程的退出与回收错误写法
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
int a=666; //局部变量--》栈空间--》函数结束的时候会自动释放它地址
printf("线程已经开始运行了\n");
//结束线程
pthread_exit(&a); //表示线程退出的时候,留下一个整数的地址给主函数(有问题,a的地址函数fun结束的时候被释放了)
}
int main()
{
//二级指针--》降级成一级指针
void *buf;
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//回收子线程
pthread_join(id,&buf);
printf("我成功回收到了子线程,子线程留下来信息是:%d\n",*((int *)buf));
}
线程的回收与退出正确写法
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
int *a=malloc(sizeof(int)); //堆空间--》函数结束的时候不会自动释放它地址
*a=666;
printf("线程已经开始运行了\n");
//结束线程
pthread_exit(a); //表示线程退出的时候,留下一个地址给主函数(没有有问题,堆空间没有释放)
}
int main()
{
//二级指针--》降级成一级指针
void *buf;
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//回收子线程
pthread_join(id,&buf);
printf("我成功回收到了子线程,子线程留下来信息是:%d\n",*((int *)buf));
}
线程的退出与回收不留下任何信息
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
printf("线程已经开始运行了\n");
//结束线程
pthread_exit(NULL); //表示线程退出的时候,不留下任何一片云彩
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//回收子线程
pthread_join(id,NULL);
}
留下字符串
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
printf("线程已经开始运行了\n");
//结束线程
pthread_exit("我退出了"); //表示线程退出的时候,留下一个字符串给主函数
}
int main()
{
//二级指针--》降级成一级指针
void *buf;
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//回收子线程
pthread_join(id,&buf);
printf("我成功回收到了子线程,子线程留下来信息是:%s\n",(char *)buf);
}
线程的取消
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
while(1)
{
printf("子线程正在运行!\n");
sleep(1);
}
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//延时5秒后,取消刚才创建的那个线程
sleep(5);
pthread_cancel(id);
//回收子线程
pthread_join(id,NULL);
}
线程设置取消类型
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
//设置子线程立即取消
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
while(1)
{
printf("子线程正在运行!\n");
sleep(1);
}
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//延时5秒后,取消刚才创建的那个线程
sleep(5);
pthread_cancel(id);
//回收子线程
pthread_join(id,NULL);
}
#include "myhead.h"
//线程的任务函数
void *fun(void *arg)
{
//设置当前子线程不可以被取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
while(1)
{
printf("子线程正在运行!\n");
sleep(1);
}
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//延时5秒后,取消刚才创建的那个线程
sleep(5);
pthread_cancel(id);
//回收子线程
pthread_join(id,NULL);
}
理解线程创建最后一个参数的使用
#include "myhead.h"
struct student
{
char name[20];
int age;
};
struct uuu
{
int a;
float b;
char c[20];
struct student stu;
};
//线程的任务函数
void *fun(void *arg)
{
//printf("线程已经开始运行了,主函数传递过来的参数是:%d\n",*((int *)arg));
//printf("线程已经开始运行了,主函数传递过来的参数是:%f\n",*((float *)arg));
//printf("线程已经开始运行了,主函数传递过来的参数是:%s\n",(char *)arg);
//struct student *p=(struct student *)arg;
//printf("线程已经开始运行了,主函数传递过来的参数是:%s %d\n",p->name,p->age);
//上面两句话压缩成一句话
//printf("线程已经开始运行了,主函数传递过来的参数是:%s %d\n",((struct student *)arg)->name,((struct student *)arg)->age);
struct uuu *q=(struct uuu *)arg;
printf("传递的信息:%d %f %s %s %d\n",q->a,q->b,q->c,q->stu.name,q->stu.age);
}
int main()
{
int a=999;
float b=98.7;
char c[20]="hello";
struct student stu={"张三",18};
struct uuu u;
u.a=8996;
u.b=45.6;
strcpy(u.c,"gec");
strcpy(u.stu.name,"李四");
u.stu.age=19;
pthread_t id;
//创建一个线程
//pthread_create(&id,NULL,fun,&a);
//pthread_create(&id,NULL,fun,&b);
//pthread_create(&id,NULL,fun,c);
//pthread_create(&id,NULL,fun,&stu);
pthread_create(&id,NULL,fun,&u);
while(1); //防止主线程退出,导致子线程挂掉了
}
push和pop的使用
#include "myhead.h"
void endfun(void *arg)
{
printf("当线程被取消的时候,该函数会被调用!\n");
}
//线程的任务函数
void *fun(void *arg)
{
pthread_cleanup_push(endfun,NULL);
while(1)
{
printf("子线程正在运行!\n");
sleep(1);
}
pthread_cleanup_pop(0);
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//延时5秒后,取消刚才创建的那个线程
sleep(5);
pthread_cancel(id);
//回收子线程
pthread_join(id,NULL);
}
pop参数设置0和非0的区别
#include "myhead.h"
void endfun(void *arg)
{
printf("当线程被取消的时候,该函数会被调用!\n");
}
//线程的任务函数
void *fun(void *arg)
{
int i;
pthread_cleanup_push(endfun,NULL);
for(i=0; i<10; i++)
{
printf("子线程正在运行!\n");
sleep(1);
}
//pthread_cleanup_pop(0); //出栈,但不执行endfun()
pthread_cleanup_pop(12); //出栈,执行endfun()
}
int main()
{
pthread_t id;
//创建一个线程
pthread_create(&id,NULL,fun,NULL);
//回收子线程
pthread_join(id,NULL);
}
prctl(PR_SET_NAME, (unsigned long)"xx");
prctl(PR_GET_NAME, name);
给当前线程取名字
#include <stdio.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <stdlib.h>
#include <unistd.h>
void* tmain(void* arg) {
char name[32];
prctl(PR_SET_NAME, (unsigned long)"xx");
prctl(PR_GET_NAME, name);
printf("%s\n", name);
while (1) {
prctl(PR_GET_NAME, name);
printf("%s\n", name);
usleep(100000);
}
}
int main(void) {
pthread_t tid;
pthread_create(&tid, NULL, tmain, NULL);
pthread_join(tid, NULL);
return 0;
}
ps -L -p pid
查看线程的名字