记录一次解决native内存泄漏的心理历程

一、概述

调试ffmpeg的stop方法时,需要等待decode解码线程执行完成:

    // 更新队列的工作状态,并唤醒阻塞中(wait)的线程
    packets.setWork(0); 
    frames.setWork(0);
    LOGE("AudioChannel::stop() pthread_join(pid_audio_decode,0)");
    // 等待pid_audio_decode的线程执行完毕
    pthread_join(pid_audio_decode,0);
    ......

然而调试时发现,卡在join这句不往下走了。

二、分析

预估是pid_audio_decode线程阻塞了,于是分析其代码片段:

void AudioChannel::decode() {
    AVPacket *packet = 0;
    LOGE("AudioChannel::decode begin");
    while (isPlaying){
        LOGE("AudioChannel::decode loop isPlaying = %d", isPlaying);
        // 取出一个数据包
        LOGE("AudioChannel::decode packets.pop(packet)");
        int ret = packets.pop(packet);
        if (!isPlaying){
            break;
        }
        if (!ret){
            continue;
        }
       ......
    }
}

通过log分析,while循环卡在这句 packets.pop(packet)了,再深入队列的pop()方法:

// 传递引用参数是为了修改它的值;用入参作为返回值可以返回多个(T& t1, T& t2...)
    int pop(T& value){
        int ret = 0;
        pthread_mutex_lock(&mutex);
        // 在多核处理器下 由于竞争可能虚假唤醒 包括jdk也说明了
        while (q.empty()){
            // 没有数据就等待
            LOGE("pop : pthread_cond_wait");
            pthread_cond_wait(&cond, &mutex);
        }
        if (!q.empty()){
            value = q.front();
            q.pop();
            ret = 1;
        }
        LOGE("pop : pthread_mutex_unlock");
        pthread_mutex_unlock(&mutex);
        return ret;
    }

有没有发现什么问题?如果队列q一直为空,即使通过条件变量通知唤醒wait操作,还是会一直循环下去,造成死循环,也就会阻塞其所在的线程,这就是pid_audio_decode线程阻塞的原因了。

三、解决方案

由于队列里面已经定义了一个是否工作的标记,可以用它来控制队列的同步操作

// 是否工作的标记 1 :工作 0:不接受数据 不工作
    int work;

pop()方法优化如下:

int pop(T& value){
        int ret = 0;
        pthread_mutex_lock(&mutex);
        // 在多核处理器下 由于竞争可能虚假唤醒 包括jdk也说明了
        // 添加work判断,如果不工作了直接跳出循环,否则会一直wait造成死循环,从而阻塞线程
        while (work && q.empty()){
            // 没有数据就等待
            LOGE("pop : pthread_cond_wait");
            pthread_cond_wait(&cond, &mutex);
        }
        if (!q.empty()){
            value = q.front();
            q.pop();
            ret = 1;
        }
        LOGE("pop : pthread_mutex_unlock");
        pthread_mutex_unlock(&mutex);
        return ret;
    }

这样优化之后,如果想要停止队列的操作,可以预先设定work标记为0,则可以避免进入死循环了。


双鱼座黄金圣斗士神圣衣

你可能感兴趣的:(记录一次解决native内存泄漏的心理历程)