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

news2024/9/28 5:31:17

目录

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. 两个宏定义,必须配对使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【重新定义matlab强大系列十四】基于问题求解有/无约束非线性优化

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

玩转Mysql系列 - 第22篇:mysql索引原理详解

这是Mysql系列第22篇。 背景 使用mysql最多的就是查询&#xff0c;我们迫切的希望mysql能查询的更快一些&#xff0c;我们经常用到的查询有&#xff1a; 按照id查询唯一一条记录 按照某些个字段查询对应的记录 查找某个范围的所有记录&#xff08;between and&#xff09; …

如何解决python安装Crypto失败的问题

文章目录 准备安装相关链接准备 Python Crypto模块是一个第三方库,它提供了常见的加密算法和协议,比如AES、RSA、DES等。 PyCrypto 是加密工具包 Crypto 的 python 版,该模块实现了各种算法和协议的加密,提供了各种加密方式对应的算法的实现,包括单向加密、对称加密及公钥…

Linux(ubuntu)系统更新后不能进入图形界面

最近需要跑一个深度学习的程序&#xff0c;把许久没用的ubuntu系统调了出来&#xff0c;手欠的我更新了一下系统&#xff0c;结果再启动&#xff0c;系统就只停留在光标闪动那里&#xff0c;不能看到图形界面了。网上查了一下&#xff0c;说是因为更新后&#xff0c;显卡驱动没…

MySQL 8.0数据库主从搭建和问题处理

错误处理&#xff1a; 在从库通过start slave启动主从复制时出现报错 Last_IO_Error: error connecting to master slaveuser10.115.30.212:3306 - retry-time: 60 retries: 1 message: Authentication plugin caching_sha2_password reported error: Authentication require…

【draw】draw.io怎么设置默认字体大小

默认情况下draw里面的字体大小是12pt&#xff0c;我们可以将字体改成我们想要的大小&#xff0c;例如18pt。 然后点击样式&#xff0c;设置为默认样式。 下一次当我们使用文字大小时就是18pt了。

html和css相关操作

html第一个网页 <!DOCTYPE html> <!--html文档声明&#xff0c;声明此文档是一个html5的文档--> <html> <!--html文档开头标签--><head><!--html文档的设置标签&#xff0c;文档的设置及资源的引用都写在这个标签中--><meta charset&q…

[论文笔记]Prefix Tuning

引言 今天带来微调LLM的第二篇论文笔记Prefix-Tuning。 作者提出了用于自然语言生成任务的prefix-tuning(前缀微调)的方法,固定语言模型的参数而优化一些连续的任务相关的向量,称为prefix。受到了语言模型提示词的启发,允许后续的token序列注意到这些prefix,当成虚拟toke…

1793_树莓派杂志第一期MagPi01阅读

全部学习汇总&#xff1a; GreyZhang/little_bits_of_raspberry_pi: my hacking trip about raspberry pi. (github.com) 给自己的产品起一个好听的名称&#xff0c;我觉得这个是国外的企业中很好的一种文化。这里提到的苹果、黑莓等全都是一系列的水果。树莓派也有这样的风格&…

命令行程序测试自动化

【软件测试面试突击班】如何逼自己一周刷完软件测试八股文教程&#xff0c;刷完面试就稳了&#xff0c;你也可以当高薪软件测试工程师&#xff08;自动化测试&#xff09; 这几天有一个小工具需要做测试&#xff0c;是一个命令行工具&#xff0c;这个命令行工具有点类似mdbg等命…

【Vue+Element-UI】实现登陆注册界面及axios之get、post请求登录功能实现、跨域问题的解决

目录 一、实现登陆注册界面 1、前期准备 2、登录静态页实现 2.1、创建Vue组件 2.2、静态页面实现 2.3、配置路由 2.4、更改App.vue样式 2.5、效果 3、注册静态页实现 3.1、静态页面实现 3.2、配置路由 3.3、效果 二、axios 1、前期准备 1.1、准备项目 1.2、安装…

pytorch学习------数据集的使用方式

一、前言 在深度学习中&#xff0c;数据量通常是都非常多&#xff0c;非常大的&#xff0c;如此大量的数据&#xff0c;不可能一次性的在模型中进行向前的计算和反向传播&#xff0c;经常我们会对整个数据进行随机的打乱顺序&#xff0c;把数据处理成一个个的batch&#xff0c…

【力扣1464】数组中两元素的最大乘积

&#x1f451;专栏内容&#xff1a;力扣刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、题目描述二、题目分析1、排序2、最值模拟 一、题目描述 题目链接&#xff1a;数组中两元素的最大乘积 给你一个整数数…

针对 SAP 的增强现实技术

增强现实技术是对现实世界的一种交互式模拟。这种功能受到各种企业和制造商的欢迎&#xff0c;因为它可以减少生产停机时间、快速发现问题并维护流程&#xff0c;从而提高运营效率。许多安卓应用都在探索增强现实技术。 使用增强现实技术&#xff08;AR&#xff09;的Liquid U…

【Vue入门】语法 —— 事件处理器、自定义组件、组件通信

目录 一、事件处理器 1.1 样式绑定 1.2 事件修饰符 1.3 按键修饰符 1.4 常用控制符 1.4.1 常用字符综合案例 1.4.2 修饰符 二、自定义组件 2.1 组件介绍及定义 2.2 组件通信 2.2.1 组件传参&#xff08;父 -> 子&#xff09; 2.2.1 组件传参&#xff08;子 ->…

【Leuanghing】ANSA入门——GUI界面介绍

【Leuanghing】ANSA入门——软件介绍 大家好&#xff01;今天为大家推荐一款软件 —— ANSA。ANSA是目前公认的全球最快捷的CAE前处理软件之一&#xff0c;也是一个功能强大的通用CAE前处理软件。 一、相关介绍 BETA CAE Systems S.A公司总部位地希腊的赛萨罗尼奇市(Thessalon…

水平基准和垂直基准

水平基准 针对不同的地理空间测量&#xff0c;水平基准可以是一个参考点或一个参考点集。在地球上&#xff0c;相同的位置上的点可能有不同的坐标&#xff0c;取决于使用了哪种基准。 水平基准用于测量地球上的位置&#xff08;position&#xff09;。因为地球不是一个完美的球…

linux————zabbix搭建

目录 一、zabbix的概述 二、构成 一、server 二、web页面 三、数据库 四、proxy 五、agent 三、zabbix监控对象 四、zabbix的常用术语 五、zabbix监控框架 一、zabbix——client架构 二、zabbix_proxy_client架构 六、zabbix部署 安装zabbix5.0存储库 ​编辑​编…

flume安装及实战

flume官方下载地址&#xff1a;Welcome to Apache Flume — Apache Flume 一、flume安装 &#xff08;1&#xff09;解压至安装目录 tar -zxf ./apache-flume-1.9.0-bin.tar.gz -C /opt/soft/ &#xff08;2&#xff09;配置文件flume-env.sh cd /opt/soft/flume190/conf …

Visual Studio 功能增强:CMake 目标视图

Visual Studio 中的 CMake 目标视图&#xff0c;允许你按 CMake 目标可视化 CMake 项目结构&#xff0c;并生成指定的目标库和可执行文件。 为了使此视图更易于使用&#xff0c;我们实施了一些新的改进&#xff0c;使导航 CMake 目标比以往任何时候都更容易。这包括改进了到 C…