pthread_cancel() 在NDK中不能使用的最佳解决方案

文章讨论了在AndroidNDK中使用pthread_kill可能导致的段错误问题,由于标准未明确规定pthread_t类型的实现,某些情况下无效线程ID可能导致错误。作者提出了通过检查线程ID的存活状态来避免这类问题,如使用SYS_gettid获取线程ID并配合ps命令检查线程是否存活。
摘要由CSDN通过智能技术生成

pthread_kill在NDK中崩溃

Android NDK在v5版本后不再提供全部的POSIX线程库的API(比如pthread_cancel和pthread_setcancelstate)。原因之一是线程被标记结束后不一定会把自己拥有的资源释放掉,甚至不一定会结束,因此很可能造成内存泄露或死锁等问题,而这些问题在移动设备上更加突出。比较安全的方法是使用更安全pthread_kill函数代替。

int kill_rc = pthread_kill(thread_id,0);
if(kill_rc == ESRCH)
printf("the specified thread did not exists or already quit\n");
else if(kill_rc == EINVAL)
printf("signal is invalid\n");
else
printf("the specified thread is alive\n");

pthread_kill引发的争议

pthread_kill会引发段错误,pthread_kill的man page描述着如果对于一个invalid pthread_t,将返回ESRCH,而不是crash才对。

有人针对这个问题向glibc nptl的开发者提了这个bug,然后作者的回答也比较直接拒了这个bug,然后两人就开始撕,单看争论的内容感觉这个确实是一个bug,大概是说文档和标准都没有这么说明,它怎么就崩溃了呢?后面又看了作者为这个问题写的一篇blogpthread_t and similar types,从posix的设计角度谈了一下为什么这么设计,写得也挺有意思。

看完后总体感觉设计者和使用者的角度还是不同的,使用者角度上接口提供的功能应该和文档描述一致,不应该隐藏一些晦涩的逻辑或者至少将功能情景描述清楚;设计者的角度则在于提供更大的灵活性和通用性,接口内不去实现特殊情况下的特定逻辑。这里实现上个人还是比较倾向设计者的角度,但对于man page的说明还是不太认同,确实写得不太清楚,不过后续还是有更新相关说明的

glibc 实现在可以检测到无效线程 ID 的情况下返回此错误。 但还要注意的是,POSIX 表示尝试使用生命周期已结束的线程 ID 会产生未定义的行为,例如,尝试在调用 pthread_kill() 时使用无效的线程 ID 可能会导致分段错误。

针对pthread_kill,其意思是如果内部检测到pthred_t是无效的则返回ESRCH,但这并不表明所有无效的pthread_t内部都能检测到,其原因是因为标准并未对pthread_t的实现类型进行明确的限制。找了glibc的pthread_kill的实现版本,发现只有tid<=0时才返回ESRCH,至于什么实时tid<=0待查(关于tid pthread_t pid tgid的区别可参考**Difference between pid and tid**),同时不同的实现的版本也有可能有区别,因此从这个角度看通过pthread_kill判断线程是否在运行貌似没有意义。。。

最终使用tid表,通过tid表可检查线程是否存活。

使用示例c代码

#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>

void  sig_handler(int signo)
{
    printf("handle sig %d \n " , signo);
    pthread_exit(NULL);
}

int main()
{
    pthread_t thread;
    int ret=pthread_create(&thread, NULL,FUN,NULL);//创建线程
    ret=pthread_kill(thread,0); //判断线程是否存活,如果杀死一个快要死掉的线程会导致段错误 
    printf("thread=%d\n",ret);
    if(ret == 0)
    {
        printf("thread alive\n");
    }
    ret=pthread_kill(thread, SIGQUIT); 
    if(ret == 0)
    {
        printf("thread  kill");
    }
}

以上代码会在linux 环境下无问题 但是在ndk编译后会出现段错误,因为篇thread_kill(pthread_t,0)去监听一个快要死掉的或者已经死掉的线程会崩溃crash。

下面通过tid表去解决代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include<sys/types.h>
#include <sys/syscall.h>



int tidpthread=0;
void  sig_handler(int signo)
{
    printf("handle sig %d \n " , signo);
    pthread_exit(NULL);
}

void *func(void *arg)
{
    signal(SIGUSR1, sig_handler); //安装信号
    unsigned int tid = syscall(SYS_gettid);
    tidpthread=tid;
    while(1)
    {
        printf("hello\n");
        sleep(1);
    }
}
pthread_t thread_create()
{   
    pthread_t tid;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
    pthread_create(&tid,&attr, func,NULL);
    return tid;
}

int main()
{
    unsigned int pidnum = getpid(); //获取pid tid
    pthread_t tid1;
    tid1=thread_create(); //线程游离态创建
    if(tid1<=0)
    {
        printf("thread_create error\n");
        return -1;
    }

    char tmpcmd[120] = {0};
    sprintf(tmpcmd, "ps -T -p %d | grep %d", pidnum, tidpthread);
           
    FILE *read_fp = popen(tmpcmd, "r");
    char ethstr[1024]={0};
    int chars_read = fread(ethstr, sizeof(char), BUFSIZ - 1, read_fp);
    pclose(read_fp);
    if ((chars_read > 0) && (tidpthread > pidnum))
    {
        pthread_kill(tid1,SIGUSR1);
    }
    else
    {
        printf("这个线程已经死亡\n");
    }
    
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值