linux 系统编程, 线程 多线程 常用API

linux线程多线程初探

进程与线程的区别

进程:

进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。
在面向线程设计的系统中,进程本身不是基本运行单位而是线程的容器

程序本身只是指令、数据及其组织形式的描述。
进程才是程序(那些指令和数据)的真正运行实例。
典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。
有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事每个线程各自处理独立的任务

线程:

线程是操作系统能够进行运算调度的最小单位。
它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程包含了表示进程内执行环境必须的信息,其中包括进程中表示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno常量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。在Unix和类Unix操作系统中线程也被称为轻量级进程(lightweight processes),但轻量级进程更多指的是内核线程(kernel thread),而把用户线程(user thread)称为线程。
在这里插入图片描述
在这里插入图片描述

进程——资源分配的最小单位,线程——程序执行的最小单位

进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。

线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

线程和进程的比较

在这里插入图片描述
在这里插入图片描述

线程的优点

进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)

相对于进程的优点:

优点一:线程的空间开销小、线程间彼此切换时间短

它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。
而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间共享大部分数据启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

优点二:线程间方便的通信机制

使用多线程的优点之二是线程间方便的通信机制。

对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。
  对于线程来说,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
  
  当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

作为多任务、并发的工作方式的其他优点:

除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

1、提高应用程序响应。

这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

2、使多CPU系统更加有效。

操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。

3、改善程序结构。

一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

多线程的用处

在这里插入图片描述
在这里插入图片描述

线程开发常用API

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。
其涉及的多线程开发的最基本概念主要包含三点:1、线程,2、互斥锁,3、条件。

线程操作
线程操作包括3 种线程的创建,退出,等待。

互斥锁
互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。

条件操作
条件操作有 5 种操作:创建,销毁,触发,广播和等待。

其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。详细请见下表:
在这里插入图片描述

线程的基本操作

就像每个进程都有一个进程号一样,每个线程也有一个线程号
进程号在整个系统中是唯一的,但线程号不同,线程号只在它所属的进程环境中有效
进程号用pid_t数据类型表示,是一个非负整数,
线程号则用pthread_t数据类型表示
有的系统在实现pthread_t的时候,用一个结构体表示,所以在可移植的操作系统实现不能把它做为整数处理 ubuntu中是整数

线程的创建

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);

功能:

创建一个线程

参数

thread: 线程标识符地址 也就是线程的id 是地址
attr: 线程属性结构体地址 默认属性设为NULL
start_routine: 线程函数的入口地址
arg: 传给线程函数的参数 也是地址

返回值

成功:返回0
失败:返回非0

当前进程也就是主控线程
与fork不同的是pthread_create创建的线程不与父线程在同一点开始运行,而是从指定的函数开始运行,该函数运行完后,该线程也就退出了。
线程依赖进程存在的,如果创建线程的进程结束了,线程也就结束了
线程函数的程序在pthread库中,故链接时要加上参数-lpthread

线程创建

#include <pthread.h>
//由于线程库原本不是系统本身的,所以在链接时需要手动链接库文件 gcc demo.c -lpthread
#include <stdio.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
void* thread_fun (void* arg)
{
    
    
        printf("子线程正在运行 \n");
}
int main()
{
    
    
        printf("主控线程正在运行\n");
        pthread_t thread;
        //通过pthread_create创建子线程
        if(pthread_create(&thread , NULL, thread_fun, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        while(1);
     	//由于进程结束后,进程中所有的线程都会强制退出,所以现阶段不要让进程退出
        return 0;
}
~      

执行结果:
在这里插入图片描述

两个线程创建

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
//一个线程中的多个线程执行顺序是不确定的,没有先后顺序可言
//多线程执行时跟进程一样,是来回切换运行的,跟进程的调度机制一样

//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
void* thread_fun1 (void* arg)
{
    
    
        printf("zi xian cheng 1 zheng zai yun xing\n");
        sleep(1);
        printf("**********************************\n");
}
void* thread_fun2 (void* arg)
{
    
    
        printf("zi xian cheng 2 zheng zai yun xing\n");
        sleep(1);
        printf("-----------------------------------\n");
}
int main()
{
    
    
        printf("zhu kong xian cheng zheng zai zhi xing\n");
        pthread_t thread1, thread2;

        if(pthread_create(&thread1 , NULL, thread_fun1, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        if(pthread_create(&thread2 , NULL, thread_fun2, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        while(1);
        return 0;
}


在这里插入图片描述

线程处理函数的传参

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
int num = 10;
//上面定义的全局变量下面两个线程函数都可以接收到,并且线程1还可以改变它的值,所以证明线程间的通信,是简单的

//线程处理函数可以认为就是一个普通的全局函数,只不过与普通函数最大的区别
//在于线程处理函数是并行处理,来回交替执行,但是普通函数是按照顺序一个个执行的
void* thread_fun1 (void* arg)
{
    
    
        printf(" 1 xiancheng num = %d\n", num);
        num++;
        int n = *(int*)arg;
        printf("n = %d\n", n);
        *(int*)arg = 111;
}
void* thread_fun2 (void* arg)
{
    
    
        sleep(1);
        printf(" 2 xiancheng num = %d\n", num);
        int n = *(int*)arg;
        printf("n = %d\n", n);

}
int main()
{
    
    
        printf("zhu kong xian cheng zheng zai zhi xing\n");
        pthread_t thread1, thread2;
        int a = 666;
        if(pthread_create(&thread1 , NULL, thread_fun1, (void *)&a)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        if(pthread_create(&thread2 , NULL, thread_fun2, (void *)&a)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        while(1);
        return 0;
}


结果如下:
在这里插入图片描述

线程等待

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

功能:

阻塞等待一个子线程的退出
可以接收到某一个子线程调用pthread_exit时设置的退出状态值

参数:

thread:指定线程的id
retval:保存子线程的退出状态值,如果不接受设为NULL

返回值:

成功返回0,失败返回非0

通过pthread_join函数等待子线程退出

#include <unistd.h>
#include <pthread.h>

//int pthread_join(pthread_t thread, void **retval);
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
void* thread_fun (void* arg)
{
    
    
        printf("zi xian cheng zheng zai yun xing\n");
        sleep(3);
        printf("zi xian cheng yao tui chu le\n");
}
int main()
{
    
    
        printf("zhu kong xian cheng zheng zai zhi xing\n");
        pthread_t thread;
        if(pthread_create(&thread , NULL, thread_fun, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        //调用pthread_join函数阻塞等待子线程退出
        if(pthread_join(thread, NULL) != 0)
        {
    
    
                perror("create pthread_join failed \n");
                exit(1);
        }
        printf("zhu kong jin cheng yao tui chu le\n");
        return 0;
}

执行的结果如下:
调用pthread_join函数阻塞等待子线程退出

在这里插入图片描述

通过pthread_join函数等待子线程退出

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
#include <pthread.h>

//int pthread_join(pthread_t thread, void **retval);
void* thread_fun (void* arg)
{
    
    
        static int as = 10;
        printf("zi xian cheng zheng zai yun xing\n");
        sleep(3);
        printf("zi xian cheng yao tui chu le\n");
        
        return (void *)&as;
        //子线程如果要返回退出状态,可以通过返回值或者通过pthread_exit函数
        //这里是通过返回值的
}
int main()
{
    
    
        printf("zhu kong xian cheng zheng zai zhi xing\n");
        pthread_t thread;
        if(pthread_create(&thread , NULL, thread_fun, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        int* num;
        if(pthread_join(thread,(void **)&num) != 0)
        {
    
    
                perror("pthread_join fail\n");
                exit(1);
        }
        printf("ret_val = %d\n", *num);
        printf("jin cheng yao tui chu le \n");
        return 0;
}

在这里插入图片描述

线程的分离

线程的结合态和分离态
linux线程执行和windows不同,pthread有两种状态
可结合的(joinable)或者是分离的(detached)线程默认创建为可结合态
如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit
时都不会释放线程所占堆栈和线程描述符
,只有当你调用了pthread_join之后这些资源才会被释放 若是detached状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放使用pthread_detach函数将线程设置为分离态。
创建一个线程后应回收其资源,但使用pthread_join函数会使调用者阻塞,故linux提供了线程分离函数。
pthread_detach

#include <pthread.h>
int pthread_detach(pthread_t thread);

功能

使调用线程与当前进程分离,使其称为一个独立的线程,该线程终止时,系统将自动回收它的资源

参数

thread: 指定子线程的id号

返回值

成功返回0,失败返回非0

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//int pthread_join(pthread_t thread, void **retval);
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
void* thread_fun (void* arg)
{
    
    
        printf("子线程正在执行\n");
        sleep(3);
        printf("子线程要推出了\n");
}
int main()
{
    
    
        printf("主控线程正在执行中\n");
        pthread_t thread;
        if(pthread_create(&thread , NULL, thread_fun, NULL)  !=  0)
        {
    
    
                perror("create fail ");
                exit(1);
        }
        通过pthread_detach函数将子线程设置为分离态,即不用堵塞,也可以自动回收子线程退出的资源
        if(pthread_detach(thread) != 0)
        {
    
    
                perror("pthread_detach fail ");
                exit(1);
        }
		如果原本子线程是结合态,需要通过pthread_join函数回收子线程推出的资源
		但是这个函数是一个阻塞函数,如果子进程不退出,就会导致当前进程(主控线程)无法继续执行
		大大的限制了代码的运行效率。
		如果子线程已经设置为分离态,就不需要再使用pthread_join了
        /*
        if(pthread_join(thread, NULL) != 0)
        {
                perror("create pthread_join failed ");
                exit(1);
        }
        */
        //这里是主控线程的执行代码,已经不受子进程堵塞了
        while(1)
        {
    
    
                printf("hello world\n");
                sleep(1);
        }
        return 0;
}

线程退出

在进程中我们可以调用exit或_exit函数来结束进程,在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流
1.线程从执行函数中返回
2.线程调用pthread_exit退出线程
3.线程可以被同一进程中的其它线程取消

线程退出函数

#include <pthread.h>
void pthread_exit(void *retval);

功能

退出调用线程

参数

retval: 存储线程退出状态的指针,也就是当前线程的退出状态值
这个值可以被调用pthread_join函数的线程接收到

一个进程中的多个线程是共享该进程的数据段,因此通常线程所占用的资源并不会释放, 如果要释放结合态需要通过pthread_join函数,分离态则自动释放

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
void* thread_fun(void* arg)
{
    
    
        printf("子线程正在执行\n");
        static char buf[] = "this is threadd quit";
        int i = 0;
        for(i=0; i<10; i++)
        {
    
    
                if(i == 5)
                {
    
    
                        //pthread_exit(NULL);//直接退出不要返回结果
                        pthread_exit((void*)buf);
                }
                printf("****************************\n");
                sleep(1);
        }
}
int main()
{
    
    
        printf("主线程正在执行\n");
        pthread_t thread;
        if(pthread_create(&thread, NULL, thread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail");
                exit(1);
        }
        //pthread_join(thread, NULL);//直接退出不要返回结果
        char* str = NULL;
        pthread_join(thread,(void**)&str);
        printf("str = %s\n", str);
        printf("主线程要推出了\n");
        return 0;
}


线程的取消

取消线程是指取消一个正在执行线程的操作

#include <pthread.h>
int pthread_cancel(pthread_t thread);

功能

取消线程

参数

thread:要销毁的线程ID

返回值

成功返回0,失败返回非0
pthread_cancel函数的实质是发信号给目标线程thread,使目标线程退
出此函数只是发送终止信号给目标线程,不会等待取消目标线程执行完才返回,然而发送成功并不意味着目标线程一定就会终止,线程被取消时,线程的取消属性会决定线程能否被取消以及何时被取消
线程的取消状态
即线程能不能被取消
线程取消点
即线程被取消的地方
线程的取消类型
在线程能被取消的状态下,是立马被取消结束还是执行到取消点的时候被取消结束

线程取消实例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

void* pthread_fun(void* arg)
{
    
    
        while(1)
        {
    
    
                printf("子线程正在执行\n");
                sleep(1);
        }
}
int main()
{
    
    
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail\n");
                exit(1);
        }
        sleep(3);
        //通过调用pthread_cancel函数取消另一个线程
        pthread_cancel(thread);
        pthread_join(thread, NULL);
        return 0;
}

执行结果
在这里插入图片描述

线程的取消状态

在linux系统下,线程默认可以被取消,编程时可以通过,

pthread_setcancelstate

函数设置线程是否可以被取消

#include <pthread.h>
pthread_setcancelstate(int state, int* old_state);

功能:设置线程是否被取消
参数:
state:新的状态
PTHREAD_CANCEL_DISABLE:不可以被取消
PTHREAD_CANCEL_ENABLE可以被取消

state
PTHREAD_CANCEL_DISABLE: 不可以被取消
PTHREAD_CANCEL_ENABLE; 可以被取消
old_state:
保存调用线程原来的可取消状态的内存地址
返回值:
成功 返回0
失败非0

具体使用实例 首先设置可以取消状态

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

void* pthread_fun(void* arg)
{
    
    
        //pthread_setcancelstate   she zhi qu xiao de zhuang tai

        //这是设置为可以取消状态
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        while(1)
        {
    
    
                printf("zi xian cheng zheng zai yun xing\n");
                sleep(1);
        }
}
int main()
{
    
    
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail\n");
                exit(1);
        }
        sleep(3);
        //通过pthread_cancel()取消线程执行
        pthread_cancel(thread);
        pthread_join(thread, NULL);
        return 0;
}

在这里插入图片描述
设置不可以取消状态

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

void* pthread_fun(void* arg)
{
    
    
        //pthread_setcancelstate 
        //设置为不可以取消状态
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
        while(1)
        {
    
    
                printf("zi xian cheng zheng zai yun xing\n");
                sleep(1);
        }
}
int main()
{
    
    
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail\n");
                exit(1);
        }
        sleep(3);
        pthread_cancel(thread);//因为上面不可以取消,所以取消不了
        pthread_join(thread, NULL);
        return 0;
}
3

执行结果
在这里插入图片描述

线程的取消点

pthread_testcancel

线程被取消后,该线程并不是马上终止,默认情况下线程执行到消点时才能被终止,编程时可以通过pthread_testcancel函数设置线程的取消点void pthread_testcancel(void)
当别的线程取消调用此函数的线程时候,被取消的线程执行到此函数时结束

#include<pthread.h>
void pthread_testcancel(void)

功能:设置线程的取消点
参数:无
返回值:无
设置取消点实例
其实很多函数执行完默认就有取消点

#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

void* pthread_fun(void* arg)
{
    
    
        while(1)
        {
    
    
                printf("zi xian cheng zheng zai yun xing\n");
                sleep(1);
                pthread_testcancel();
        }
}
int main()
{
    
    
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail\n");
                exit(1);
        }
        sleep(3);
        pthread_cancel(thread);

        pthread_join(thread, NULL);
        return 0;
}

执行结果
在这里插入图片描述

线程的取消类型

线程被取消后,该线程并不是马上终止,默认情况下线程执行到消点时才能被终止,编程时可以通过pthread_setcanceltype函数设置线程是否可以立即被取消

#include <pthread.h>
int pthread_setcanceltype(int type,int *oldtype);

功能:设置线程是否可以立即被取消
参数
type:类型
PTHREAD_CANCEL_ASYNCHRONOUS 立即取消
PTHREAD_CANCEL_DEFERRED 不立即被取消
oldtype 保存调用线程原来的可取消类型的内存地址
返回值
成功: 返回0
失败: 返回非0

具体实例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

void* pthread_fun(void* arg)
{
    
    
        //设置线程权限为可以被取消
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);

        //设置线程取消的类型
        //设置为立即被取消
        //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
        
        //设置为不立即被取消
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED ,NULL);
        while(1)
        {
    
    
                printf("zi xian cheng zheng zai yun xing\n");
                sleep(1);
        }
}
int main()
{
    
    
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
        {
    
    
                perror("pthread_create fail\n");
                exit(1);
        }
        sleep(3);

        pthread_cancel(thread);
        pthread_join(thread, NULL);
        return 0;
}

结果如下所示
在这里插入图片描述

基本上工作中项目中直接使用pthread_cancel基本都可以取消

线程退出清理函数

和进程的退出清理一样,线程也可以注册它退出时要调用的函数,这样的函数称为线程清理处理程序(thread clearup handler)
注意
线程可以建立多个清理处理程序
处理程序在栈中,故它们的执行顺序与它们注册时的顺序相反

注册清理函数 pthread_cleanup_push

#include <pthread.h>
 void pthread_cleanup_push(void (*routine)(void *), void *arg);

功能

将清理函数压栈,即注册清理函数

参数

routine: 线程清理函数的指针
传给线程清理函数的参数

弹出清理函数 pthread_cleanup_pop

#include<pthread.h>
void pthread_cleanup_pop(int execute);

功能

将清楚函数弹栈,即删除清理函数

参数

execute: 线程清理函数执行标志位
非0,弹出清理函数,执行清理函数
0 ,弹出清理函数, 不执行清理函数

当线程执行以下动作时会调用线程退出清理函数

1.调用pthread_exit退出线程
2.响应其它线程的取消请求
3.用非零execute 调用pthread_cleanup_pop
无论哪种情况pthread_cleanup_pop都将删除上一次pthread_clean_push调用注册的清理处理函数

案例1,验证线程调用pthread_exit函数时,系统自动调用线程清理函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void myclearup(void* arg)//线程退出清理函数
{
    
    
        printf("clean up ptr = %s\n", (char*)arg);
        free((char*)arg);
}
void* thread(void *arg)//线程处理函数
{
    
    
        //建立线程清理程序
        printf("this is new thread\n");
        char* ptr = NULL;
        ptr = (char*)malloc(100);
         //将清理函数进行压栈操作,调用清理函数myclearup并进行传参ptr
        pthread_cleanup_push(myclearup,  (void*)(ptr));
       
        bzero(ptr, 100);//将字符串里面的内容清空
        strcpy(ptr, "memory from malloc");

        sleep(3);
        printf("before exit\n");
        pthread_exit(NULL);
        //注意push与pop必须配对使用,即使pop执行不到
        printf("before pop\n");
        pthread_cleanup_pop(1);
}
int main()
{
    
    
        pthread_t tid;
        pthread_create(&tid, NULL , thread, NULL);//创建一个线程
        pthread_join(tid,NULL);//阻塞等待子线程的退出
        printf(" process is dying\n");
        return 0;
}

执行结果
在这里插入图片描述

案例2,验证线程被取消时,系统自动调用线程清理函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void myclearup(void* arg)
{
    
    
        printf("clean up ptr = %s\n", (char*)arg);
        free((char*)arg);
}
void* thread(void *arg)
{
    
    
        printf("this is new thread\n");
        char* ptr = NULL;
        ptr = (char*)malloc(100);
        pthread_cleanup_push(myclearup,  (void*)(ptr));
        bzero(ptr, 100);
        strcpy(ptr, "memory from malloc");

        sleep(10);
        printf("before pop\n");
        pthread_cleanup_pop(1);
        return NULL;
}
int main()
{
    
    
        pthread_t tid;
        pthread_create(&tid, NULL , thread, NULL);
        sleep(5);
        printf("before cancel\n");
        pthread_cancel(tid);
        pthread_join(tid,NULL);
        printf(" process is dying\n");
        return 0;
}

结果如下所示
在这里插入图片描述

案例3,验证调用pthread_cleanup_pop函数时,系统自动调用线程清理函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

void cleanup_func1(void* arg)
{
    
    
        printf("in cleanup fun1\n");
        printf("clean up ptr = %s\n", (char*)arg);
        free((char*)arg);
}
void cleanup_func2(void* arg)
{
    
    
        printf("in cleanup func2\n");
}
void* thread(void* arge)
{
    
    
        char* ptr = NULL;
        printf("this is new thread\n");
        ptr = (char*)malloc(100);
        pthread_cleanup_push(cleanup_func1,(void*)(ptr));
        pthread_cleanup_push(cleanup_func2,NULL);
        bzero(ptr, 100);
        strcpy(ptr, "memory from malloc");
        sleep(3);

        printf("before pop\n");
        pthread_cleanup_pop(1);

        printf("before pop\n");//jinxing tan zhan 
        pthread_cleanup_pop(1);
        return NULL;
}
int main()
{
    
    
        pthread_t tid;
        pthread_create(&tid, NULL, thread, NULL);
        pthread_join(tid, NULL);
        printf("process is dying\n");
        return 0;
}

结果如下
在这里插入图片描述
因为栈是先进的后出,所以先创建fun1,但是先执行fun2

猜你喜欢

转载自blog.csdn.net/weixin_52495715/article/details/121774475