lv5 嵌入式开发-5 线程的创建和参数传递

目录

1 线程基础

2 Linux线程库

2.1 线程创建 – pthread_create

2.2 线程结束 – pthread_exit

2.3 线程查看tid函数

2.4 线程间参数传递(重点)

2.4.1 练习

2.5 线程查看命令(多线程)

2.6 线程回收 – pthread_join

2.7 线程分离pthead_detach和设置属性

2.7.1 pthead_detach方式

2.7.1 设置Attr属性

2.8 取消一个线程

2.8.1 pthread_cancel

 2.8.2 pthread_testcancel

2.8.3 pthread_setcancelstate

2.8.4 pthread_setcanceltype

2.9 线程的清理


掌握:线程概念、线程特点、线程创建

1 线程基础

线程概念

进程

  • 进程有独立的地址空间
  • Linux为每个进程创建task_struct
  • 每个进程都参与内核调度,互不影响

线程

  • 进程在切换时系统开销大
  • 很多操作系统引入了轻量级进程LWP
  • 同一进程中的线程共享相同地址空间
  • Linux不区分进程、线程

线程特点

  • 通常线程指的是共享相同地址空间的多个任务

使用多线程的好处

  • 大大提高了任务切换的效率
  • 避免了额外的TLB & cache的刷新

线程共享资源

一个进程中的多个线程共享以下资源:

  • 可执行的指令
  • 静态数据(如果再一个进程里定义的全局变量,在进程里所有线程都能访问,进程A里定义的全局变量,在进程B里是不能访问的,包括子进程也不行
  • 进程中打开的文件描述符
  • 当前工作目录
  • 用户ID
  • 用户组ID

线程私有资源

每个线程私有的资源包括:

  • 线程ID (TID)
  • PC(程序计数器)和相关寄存器
  • 堆栈(局部变量)
  • 错误号 (errno)
  • 优先级
  • 执行状态和属性

2 Linux线程库

linux内核中并没有实现线程,所以通过线程库实现

pthread线程库中提供了如下基本操作

  • 创建线程
  • 回收线程
  • 结束线程

同步和互斥机制

  • 信号量
  • 互斥锁

2.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 线程执行的函数  (回调函数,*routine 是函数指针 输入参数和返回值都是void*)
  • 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);
}

//如果不加printf和sleep1,主线程结束,子线程也结束可能就打印不出内容。
linux@linux:~/Desktop$ gcc -o pthread pthread.c -lpthread
linux@linux:~/Desktop$ ./pthread 
linux@linux:~/Desktop$ ./pthread 

编译错误分析:

createP_t.c:14:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]

     ret = pthread_create(&tid,NULL,testThread,NULL);

                                    ^

In file included from createP_t.c:1:0:

/usr/include/pthread.h:233:12: note: expected ‘void * (*)(void *)’ but argument is of type ‘int * (*)(char *)’

意义:表示pthread_create参数3的定义和实际代码不符合,期望的是void * (*)(void *) ,实际的代码是int * (*)(char *)

解决方法:改为pthread_create(&tid,NULL,(void*)testThread,NULL);

createP_t.c:(.text+0x4b):对‘pthread_create’未定义的引用

collect2: error: ld returned 1 exit status   --------这个链接错误,

表示pthread_create这个函数没有实现

解决方法:编译时候加 -lpthread

注意事项:1. 主进程的退出,它创建的线程也会退出。

线程创建需要时间,如果主进程马上退出,那线程不能得到执行

2.2 线程结束 – pthread_exit

 #include  <pthread.h>
 void  pthread_exit(void *retval);
  • 结束当前线程  
  • retval可被其他线程通过pthread_join获取  
  • 线程私有资源被释放

示例

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

void *testThread(void *arg){
    printf("This is a thread test\n");
    pthread_exit(NULL);
    printf("after pthread exit\n");   //不会被打印,线程已经清理
}
int main(){
    pthread_t tid;
    int ret;
    int arg = 5;
   
    ret = pthread_create(&tid,NULL,testThread,(void *)arg);

    printf("This is main thread\n");    
    sleep(1);
}



//运行结果
linux@linux:~/Desktop$ gcc -o pthread pthread.c -lpthread
linux@linux:~/Desktop$ ./pthread 
This is main thread
This is a thread test

2.3 线程查看tid函数

pthread_t  pthread_self(void)   查看自己的TID
#include <pthread.h>
pthread_t pthread_self(void);

 示例:

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

void *testThread(void *arg){
    printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
   // return NULL;

    pthread_exit(NULL);
    printf("after pthread exit\n");
}
int main(){
    pthread_t tid;
    int ret;
   
    ret = pthread_create(&tid,NULL,testThread,(void *)arg);

    printf("This is main thread,tid=%lu\n",tid);    
    sleep(1);
}

2.4 线程间参数传递(重点)

pthread_create(pthread_t *thread, const
       pthread_attr_t *attr, void *(*routine)(void *), void *arg);

最后一个参数

示例:

//方式1
//直接传参数值也可以,把数当地址来传入了。因为int和指针都是4字节,如果是long就不行了
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void *testThread(void *arg){
    printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
   // return NULL;
    printf("input arg=%d\n",(int)arg);
    pthread_exit(NULL);
    printf("after pthread exit\n");
}
int main(){
    pthread_t tid;
    int ret;
    int arg = 5;
   
    ret = pthread_create(&tid,NULL,testThread,(void *)arg);

    printf("This is main thread,tid=%lu\n",tid);    
    sleep(1);
}


//方式2
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void *testThread(void *arg){
    printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
   // return NULL;
    printf("input arg=%d\n",*(int *)arg);
    pthread_exit(NULL);
    printf("after pthread exit\n");
}
int main(){
    pthread_t tid;
    int ret;
    int arg = 5;
   
    ret = pthread_create(&tid,NULL,testThread,(void *)&arg);

    printf("This is main thread,tid=%lu\n",tid);    
    sleep(1);
}

补充:

编译错误:

createP_t.c:8:34: warning: dereferencing ‘void *’ pointer

     printf("input arg=%d\n",(int)*arg);

                                ^

createP_t.c:8:5: error: invalid use of void expression

     printf("input arg=%d\n",(int)*arg);

错误原因是void *类型指针不能直接用*取值(*arg),因为编译不知道数据类型。

解决方法:转换为指定的指针类型后再用*取值  比如:*(int *)arg

  1. 通过地址传递参数,注意类型的转换
  2. 值传递,这时候编译器会告警,需要程序员自己保证数据长度正确

示例:

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

void *testThread(void *arg){
    printf("This is a thread test,pid=%d,tid=%lu\n",getpid(),pthread_self());
   // return NULL;
    printf("This is %d thread.\n", (int)arg);
   // pthread_exit(NULL);
    while(1){
        sleep(1);
    }
    printf("after pthread exit\n");
}
int main(){
    pthread_t tid[5];
    int ret;
    int arg = 5;
    int i;
    for(i=0;i<5;i++){   
        ret = pthread_create(&tid[i],NULL,testThread,(void *)i);
     
//        sleep(1);   //执行效率低无法打印i的值,因为传入的是地址,还没来得及改变,可以改用传值
        printf("This is main thread,tid=%lu\n",tid[i]);    
    }
    while(1){
        sleep(1);
    }
}

运行错误:

*** stack smashing detected ***: ./mthread_t terminated

已放弃 (核心已转储)

原因:栈被破坏了(数组越界)

2.4.1 练习

使用pthread_create实现 10 个子线程,并且让每个子线程打印自己的线程号

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

int *Thread_fun(char *arg)
{
	printf("This is a thread test.pid=%d,tid=%lu,\n",getpid(),pthread_self());
	printf("This is %d thread.\n",(int)arg);
	while(1)
	{
		sleep(1);
	}
	printf("after pthread exit\n");
	return 0;
}


int main(int argc, char * argv[])
{
	int i;
	pthread_t tid[10];
	int ret = 0;

	for(i = 0; i < 10; i++)
	{	
		ret = pthread_create(&tid[i],NULL,(void *)Thread_fun,(void *)i);
		printf("This i main thread,tid=%lu\n",tid[i]);
	}
	while(1)
	{
		sleep(1);
	}

}

2.5 线程查看命令(多线程)

ps -eLf

 示例

2.6 线程回收 – pthread_join

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

对于一个默认属性的线程 A 来说,线程占用的资源并不会因为执行结束而得到释放

  • 成功返回0,失败时返回错误码  
  • thread 要回收的线程对象(ID号)  
  • 调用线程阻塞直到thread结束  
  • *retval 接收线程thread的返回值

注意:pthread_join 是阻塞函数,如果回收的线程没有结束,则一直等待

示例

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

void *Thread_fun(void *arg)
{
	printf("This is a child thread\n");
	sleep(1);
	pthread_exit("thread return");
}


int main(int argc, char * argv[])
{
	pthread_t tid;
	void *retv;

	pthread_create(&tid,NULL,Thread_fun,NULL);
	pthread_join(tid,&retv);
	printf("thread ret=%s\n",(char *)retv);
	sleep(1);

}


//运行结果
linux@linux:~/Desktop$ ./test_pthread 
This is a child thread
thread ret=thread return

多个线程回收 

#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);
        printf("thread ret=%s\n",(char*)retv);
    }
    while(1){    
        sleep(1);
    } 

}

回收效果,使用top命令

linux@linux:~$ ps -ef|grep "pjoin"
linux    16330  2072  0 10:46 ?        00:00:01 gedit /home/linux/Desktop/pjoin.c
linux    16467  2732  0 10:50 pts/9    00:00:00 ./pjoin
linux    16573 15358  0 10:51 pts/1    00:00:00 grep --color=auto pjoin
linux@linux:~$ top -p 16467

发现回收前后,虚拟内存减小,实际内存减小,如果不使用回收,内存不会有变化还可能变大。

2.7 线程分离pthead_detach和设置属性

使用线程的分离两种方式:

1 使用pthread_detach

2 创建线程时候设置为分离属性

2.7.1 pthead_detach方式

int pthread_detach(pthread_t thread);    

成功:0;失败:错误号
指定该状态,线程主动与主控线程断开关系。线程结束后(不会产生僵尸线程

此种方式不需要通过主线程取回收线程资源。
示例

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    pthread_detach(pthread_self());   //方式2
    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);
        //pthread_detach(tid);  //方式1
    }
    
    while(1){    
        sleep(1);
    } 

}

 实际效果:同2.6回收效果,虚拟内存减小,实际内存减小,如果不使用detach,内存不会有变化还可能变大。

2.7.1 设置Attr属性

 pthread_attr_t attr;   /*通过线程属性来设置游离态(分离态)*/

设置线程属性为分离

pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

示例:

#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;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);  //分离属性

    for(i=0;i<100;i++){
        pthread_create(&tid[i],&attr,func,NULL);
       // pthread_detach(tid);
    }
    
    while(1){    
        sleep(1);
    } 

}

线程分离的目的: 

  1. 资源回收:当线程被标记为可分离(detached)时,线程退出后,系统会自动回收其占用的资源,无需其他线程等待或执行特定的回收操作。这对于长时间运行或创建大量线程的应用程序很有用,因为它可以减少资源泄漏的风险。

  2. 线程管理:标记线程为可分离使得其独立于主线程或其他线程而存在,它可以自主地运行和结束,不会影响其他线程的正常执行。这对于一些需要并发执行的任务或周期性任务非常有用,可以提高整体的系统性能和响应能力。

需要注意的是,线程创建时的默认属性是非分离(joinable),即需要使用 pthread_join 函数来等待线程的结束并回收资源。如果需要将线程设置为可分离属性,可以使用 pthread_attr_setdetachstate 函数将属性设置为 PTHREAD_CREATE_DETACHED

2.8 取消一个线程

2.8.1 pthread_cancel

int pthread_cancel(pthread_t thread);     杀死一个线程

作用:如果一个线程是个死循环,那么exit可能永远也执行不到。这种情况需要取消线程的功能。

意义:随时杀掉一个线程,如果使用kill命令,会把进程也一起杀掉。

注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用

示例:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    printf("This is child thread\n");
    while(1)
    {
        sleep(5);

    }
    pthread_exit("thread return");  //永远不会执行
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(5);
    pthread_cancel(tid);
    pthread_join(tid,&retv);
    printf("thread ret=%s\n",(char*)retv);  //这里会出现段错误,这是空指针
    while(1){    
        sleep(1);
    } 

}

运行段错误调试:

可以使用gdb调试

使用gdb 运行代码,gdb  ./youapp

(gdb) run

等待出现Thread 1 "pcancel" received signal SIGSEGV, Segmentation fault.

输入命令bt(打印调用栈)

(gdb) bt

#0  0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux-gnu/libc.so.6  //vfprintf报错

#1  0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6  //#2调用了#1

#2  0x00000000004007f9 in main () at pcancel.c:21   //栈底

确定段错误位置是pcancel.c 21行

注释掉后调试结果

linux@linux:~$ ps -eLf|grep "pcancel"
linux     3467  2962  3467  0    2 02:50 pts/7    00:00:00 ./pcancel
linux     3467  2962  3468  0    2 02:50 pts/7    00:00:00 ./pcancel
linux     3470  3184  3470  0    1 02:50 pts/2    00:00:00 grep --color=auto pcancel
linux@linux:~$ ps -eLf|grep "pcancel"
linux     3467  2962  3467  0    2 02:50 pts/7    00:00:00 ./pcancel
linux     3467  2962  3468  0    2 02:50 pts/7    00:00:00 ./pcancel
linux     3472  3184  3472  0    1 02:50 pts/2    00:00:00 grep --color=auto pcancel
linux@linux:~$ ps -eLf|grep "pcancel"
linux     3467  2962  3467  0    1 02:50 pts/7    00:00:00 ./pcancel
linux     3474  3184  3474  0    1 02:50 pts/2    00:00:00 grep --color=auto pcancel
linux@linux:~$ 

 2.8.2 pthread_testcancel

如果没有取消点,手动设置一个

void pthread_testcancel(void);
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    printf("This is child thread\n");
    while(1)
    {
	    pthread_testcancel();  //如果程序代码很长,找不到死循环的位置,可以用这段代码
    }
    pthread_exit("thread return");  //永远不会执行
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(5);
    pthread_cancel(tid);
 
    while(1){    
        sleep(1);
    } 

}

如果没有取消点,手动设置一个

2.8.3 pthread_setcancelstate

目的:让有些代码可以被取消,有些代码不能被取消。有些先后顺序不好找的时候。

int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    printf("This is child thread\n");
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);   //不能被取消,不管有没有阻塞点
    while(1)
    {
        sleep(5);
        pthread_testcancel();
    }
    pthread_exit("thread return");  //永远不会执行
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);
    pthread_cancel(tid);
    pthread_join(tid,&retv);
//    printf("thread ret=%s\n",(char*)retv);
    while(1){    
        sleep(1);
    } 

}


//运行结果
linux@linux:~$ ps -eLf|grep "pcancel"
linux     6271  2962  6271  0    2 03:43 pts/7    00:00:00 ./pcancel
linux     6271  2962  6272  0    2 03:43 pts/7    00:00:00 ./pcancel
linux     6287  3184  6287  0    1 03:46 pts/2    00:00:00 grep --color=auto pcancel

此时在设置可以取消。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void *func(void *arg){
    printf("This is child thread\n");
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    sleep(5);
    pthread_testcancel();               //如果程序代码很长,找不到死循环的位置,可以用这段代码
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);     //可以取消了
    pthread_exit("thread return");  //永远不会执行
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(5);             //5秒内不能被取消,可以执行完上述代码
    pthread_cancel(tid);
 
    while(1){    
        sleep(1);
    } 

}

总结:

能不能取消却决于有没有取消点,pthread_testcancel(),同时设置是否能被取消。

如果不能被取消,则有没有取消点都没有关系了。

2.8.4 pthread_setcanceltype

int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED       等到取消点才取消         
PTHREAD_CANCEL_ASYNCHRONOUS   目标线程会立刻取消

2.9 线程的清理

问题:如果线程取消了,代码没有正常退出,内存没有释放,会造成内存泄漏怎么解决?

必要性:当线程非正常终止,需要清理一些资源。

void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)

两个函数需要成对出现再代码中,否则会出错:

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

void cleanup(void *arg)
{
	printf("cleanup=%s\n",(char*)arg);
}

void *func(void *arg)
{
    printf("This is child thread\n");
    pthread_cleanup_push(cleanup,"abcd");
    pthread_exit("thread return");  
}


int main()
{
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);   
    pthread_cancel(tid);	          
    pthread_join(tid,&revt);	    

    while(1)
	{    
        sleep(1);
    } 

}

linux@linux:~/Desktop$ gcc -g -o pcancel pcancel.c -lpthread
pcancel.c: In function ‘func’:
pcancel.c:18:1: error: expected ‘while’ before ‘int’
 int main()
 ^
pcancel.c:33:1: error: expected declaration or statement at end of input
 }
 ^
pcancel.c:33:1: error: expected declaration or statement at end of input

查看vim /usr/include/pthread.h 

正确用法:

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

void cleanup(void *arg)
{
    //做清理工作的代码
	printf("cleanup=%s\n",(char*)arg);
}

void *func(void *arg)
{
    printf("This is child thread\n");
    pthread_cleanup_push(cleanup,"abcd");
    pthread_exit("thread return");   //执行这句话,上面cleanup也会被调用到。
    pthread_cleanup_pop(0);          //此时这句话意义在于完成大括号结束,0代表删除函数
    
}

/*
void *func(void *arg)
{
    printf("This is child thread\n");
    pthread_cleanup_push(cleanup,"abcd");
    pthread_cleanup_pop(1);          // 非0参数执行pthread_cleanup_pop()
    pthread_exit("thread return");   
                
}
*/

int main()
{
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);   
    pthread_cancel(tid);	          
    pthread_join(tid,&revt);	    

    while(1)
	{    
        sleep(1);
    } 

}

//运行结果
linux@linux:~/Desktop$ ./pcancel 
This is child thread
cleanup=abcd

routine 函数被执行的条件:

  1. 被pthread_cancel取消掉。
  2. 执行pthread_exit
  3. 非0参数执行pthread_cleanup_pop()

注意:

  • 必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。
  • pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.
  • pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反(先入后出)
  • 线程内的return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void cleanup(void *arg){
    printf("cleanup,arg=%s\n",(char*)arg);

}
void cleanup2(void* arg){

    printf("cleanup2,arg=%s\n",(char*)arg);
}

void *func(void *arg){
    printf("This is child thread\n");
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);  //设置了遇到取消点立刻取消线程
    pthread_cleanup_push(cleanup,"abcd");
    pthread_cleanup_push(cleanup2,"efgh");
    //while(1)
    {
        sleep(1);
        
    }
    pthread_cancel(pthread_self());                           //取消线程,也会触发回调
    printf("Should not print\n");

    while(1){
        printf("sleep\n");
        sleep(1);
    }
    pthread_exit("thread return");
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    sleep(10);
    pthread_exit("thread return");
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);
//    pthread_cancel(tid);
    pthread_join(tid,&retv);
    //printf("thread ret=%s\n",(char*)retv);
    while(1){    
        sleep(1);
    } 

}

//运行结果
linux@linux:~/Desktop$ ./pcancel 
This is child thread
cleanup2,arg=efgh
cleanup,arg=abcd

如改变一下:return和 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。

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

void cleanup(void *arg){
    printf("cleanup,arg=%s\n",(char*)arg);

}
void cleanup2(void* arg){

    printf("cleanup2,arg=%s\n",(char*)arg);
}

void *func(void *arg){
    printf("This is child thread\n");
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
    pthread_cleanup_push(cleanup,"abcd");
    pthread_cleanup_push(cleanup2,"efgh");
    //while(1)
    {
        sleep(1);
        
    }
//    pthread_cancel(pthread_self());
    //printf("Should not print\n");
    return "1234";

    while(1){
        printf("sleep\n");
        sleep(1);
    }
    pthread_exit("thread return");
    pthread_cleanup_pop(1);
    pthread_cleanup_pop(1);
    sleep(10);
    pthread_exit("thread return");
}


int main(){
    pthread_t tid;
    void *retv;
    int i;
    pthread_create(&tid,NULL,func,NULL);
    sleep(1);
//    pthread_cancel(tid);
    pthread_join(tid,&retv);
    printf("thread ret=%s\n",(char*)retv);
    while(1){    
        sleep(1);
    } 

}

练习

void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute) 的本质是什么?D
A. 两个函数
B. pthread_cleanup_push函数可以取消一个线程
C. pthread_cleanup_pop函数可以清理一个线程
D. 两个宏定义,必须配对使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

4IOT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值