Android通用C++线程池

一、背景意义       

        项目中可能需要处理一些比较繁琐的事,而这些事正好可以使用线程来解决,如tv上经常会去dump  raw data,若直接在解码线程中dump raw data会导致每一帧的处理时间变长,如果大幅超出33ms,就会出现丢帧情况,而对于h264格式数据来说,丢帧会导致马赛克产生。下面列出线程池的一种实现(部分功能可以再完善下,比如添加job的优先级,然后根据该优先级顺序来处理job......),此文件导入到项目中就可以使用。

二、具体实现

ThreadCore.h

/*
 *Inc. (C) 2021. All rights reserved.
 *
 */

#ifndef ANDROID_THREADCORE_H
#define ANDROID_THREADCORE_H

#include <list>
#include <mutex>
#include <condition_variable>
#include <log/log.h>
#include "utils/Thread.h"
#include "utils/Mutex.h"
#include "utils/Timers.h"
#include "Base.h"

namespace android {

#define THRAED_MAX          2
#define JOB_ENTRY_MAX_SIZE  8

// Opaque handle to a job
typedef uint64_t JobHandle;

// Function pointer type for a job function
typedef void (*JobFunc)(void* pArg);

// Function pointer type for a job callback function
typedef void (*JobCb)(void* pArg);

// Maximum number of job list
static size_t MaxJobCount = 20;

// Relative priority of a job submitted
enum struct JobPriority
{
    Critical = 0,   // Critical priority job
    High     = 1,   // High priority
    Normal   = 2,   // Normal priority
    Invalid  = 3    // Invalid priority
};

// Describes the functions to be executed and their priorities
struct JobEntry {
    JobFunc pFuncAddr;
    JobPriority priority;
    JobHandle handle;
    JobCb pCb;
    bool used = false;
};

// Describe the job and their data
struct JobData {
    JobHandle handle;
    void* pData;
};

// Describe the job func to be performed immediately and their data
struct RuntimeJob {
    JobFunc pFuncAddr;
    JobCb pCb;
    void* pData;
};

// JobManager provider
class JobManager : public virtual RefBase {
  public:
    JobManager(){};
    ~JobManager(){};
    bool registerNewJob(JobFunc jobFuncAddr, JobCb jobCbAddr, JobPriority priority, JobHandle* handle);
    void releaseRuntimeJob(RuntimeJob* job);
    JobEntry* getJobByHandle(JobHandle jobHandle);
    JobEntry* getAllJob(){return mJobSlot;};
    JobHandle packJobHandle(uint32_t idx);
    uint32_t getSlotByHandle(JobHandle handle);
    bool isJobRegistered(JobHandle handle);
    size_t getJobCount();
    bool checkJobsOnHold();
    bool enqueueJob(JobData jobData);
    RuntimeJob* dequeueJob();
    void flushJob();

  private:
    mutable std::mutex mJobLock;
    std::list<JobData> mJobHandleList;
    JobEntry mJobSlot[JOB_ENTRY_MAX_SIZE];
};

// ThreadManager provider
class ThreadManager : public virtual RefBase {
  public:
    ThreadManager();
    ~ThreadManager();

    void initialize(std::string name);
    bool registerNewJob(JobFunc jobFuncAddr, JobCb jobCbAddr, JobPriority priority, JobHandle* handle);
    bool isRunning(){ return mRunning;};
    bool postJob(JobData jobData);
    size_t getJobCount();
    status_t startThreads();
    void stopThreads();
    void flush();
    void doWork(pid_t tid, std::string threadName);


    class ThreadFunc : public Thread {
      public:
        ThreadFunc(wp<ThreadManager> parent, char name[]):mParent(parent),mName(name){};
        ~ThreadFunc();
        virtual bool threadLoop() override;
        std::string getThreadName(){return mName;};
        void stop();
      private:
		std::string mName;
        // use weak_ptr, avoid memory leak
        const wp<ThreadManager> mParent;
    };

  private:
    sp<ThreadFunc> mThreads[THRAED_MAX];
    mutable std::mutex mWorkMutex;
    std::condition_variable mWorkCond;
    JobManager mJobManager;
    std::string mName;
    bool mPending = false;
    bool mRunning = true;
};

}

#endif

ThreadCore.cpp

/*
 * Inc. (C) 2021. All rights reserved.
 *
 */

#include "ThreadCore.h"

#define LOG_NDEBUG 0
//#define LOG_NNDEBUG 0

#define LOG_TAG "ThreadCore"
//#define ALOGV(...) ALOGI(__VA_ARGS__)

namespace android {

bool JobManager::registerNewJob(JobFunc jobFuncAddr, JobCb jobCbAddr,
    JobPriority priority, JobHandle* handle) {
    uint32_t idx;
    for (idx = 0;idx < JOB_ENTRY_MAX_SIZE;idx++) {
        if (!mJobSlot[idx].used) {
            *handle = packJobHandle(idx);
            mJobSlot[idx].pFuncAddr = jobFuncAddr;
            mJobSlot[idx].priority = priority;
            mJobSlot[idx].handle = *handle;
            mJobSlot[idx].pCb= jobCbAddr;
            mJobSlot[idx].used = true;
            break;
        }
    }

    if (idx >= JOB_ENTRY_MAX_SIZE) {
        ALOGE("%s: register job failed due to the number of job exceeds %d",
            __FUNCTION__, JOB_ENTRY_MAX_SIZE);
        return false;
    }

    return true;
}

JobHandle JobManager::packJobHandle(uint32_t idx) {
    JobHandle handle = 1 << 32;
    handle |= idx + 1;
    return handle;
}

JobEntry* JobManager::getJobByHandle(JobHandle handle) {
    uint32_t idx = (handle & 0xF) - 1;
    if (idx <= JOB_ENTRY_MAX_SIZE) {
        return &mJobSlot[idx];
    }
    else {
        return NULL;
    }
}

// This method needs to be implemented
bool JobManager::isJobRegistered(JobHandle handle) {
    return false;
}

bool JobManager::checkJobsOnHold() {
    std::lock_guard<std::mutex> lock(mJobLock);
    return !mJobHandleList.empty();
}

bool JobManager::enqueueJob(JobData jobData) {
    std::lock_guard<std::mutex> lock(mJobLock);
    if (mJobHandleList.size() < MaxJobCount) {
        mJobHandleList.push_back(jobData);
        return true;
    }
    return false;
}

RuntimeJob* JobManager::dequeueJob() {;
    std::unique_lock<std::mutex> lock(mJobLock);
    JobData jobData;
    if (!mJobHandleList.empty()) {
        jobData = mJobHandleList.front();
        mJobHandleList.pop_front();

        JobEntry* jobEntry = getJobByHandle(jobData.handle);
        if (NULL != jobEntry) {
            RuntimeJob* currentJob = (RuntimeJob*)malloc(sizeof(RuntimeJob));
            currentJob->pFuncAddr = jobEntry->pFuncAddr;
            currentJob->pCb = jobEntry->pCb;
            currentJob->pData = jobData.pData;
            return currentJob;
        }
    }
    lock.unlock();
    return NULL;
}

// This function may have asynchronous security problems
// For the sake of program efficiency, it can be ignored
size_t JobManager::getJobCount() {
    return mJobHandleList.size();
}

void JobManager::flushJob() {
    ALOGV("+[%s]", __FUNCTION__);
    std::lock_guard<std::mutex> lock(mJobLock);
    for (auto iter = mJobHandleList.begin(); iter != mJobHandleList.end(); iter++) {
        JobEntry* jobEntry = getJobByHandle(iter->handle);
        if (NULL != jobEntry->pCb) {
            jobEntry->pCb(iter->pData);
        }
        free(iter->pData);
        iter = mJobHandleList.erase(iter);
    }
    ALOGV("-[%s]", __FUNCTION__);
}

void JobManager::releaseRuntimeJob(RuntimeJob* job) {
    if (NULL != job) {
        if (NULL != job->pData) {
            free(job->pData);
			job->pData = NULL;
        }
        free(job);
    }
}

ThreadManager::ThreadManager() {
    ALOGV("+[%s]", __FUNCTION__);
    ALOGV("-[%s]", __FUNCTION__);
}

ThreadManager::~ThreadManager() {
    ALOGV("+[%s]", __FUNCTION__);
    ALOGV("-[%s]", __FUNCTION__);
}

void ThreadManager::initialize(std::string name) {
    mName = name;
    for (uint32_t i = 0;i < THRAED_MAX;i++) {
        char threadName[64];
        snprintf(threadName, sizeof(threadName), "%s_%d", mName.c_str(), i);
        mThreads[i] = new ThreadFunc(this, threadName);
    }
}

bool ThreadManager::registerNewJob(JobFunc jobFuncAddr, JobCb jobCbAddr,
    JobPriority priority, JobHandle* handle) {
    return mJobManager.registerNewJob(jobFuncAddr, jobCbAddr, priority, handle);
}

bool ThreadManager::postJob(JobData jobData) {
    ALOGV("+[%s]", __FUNCTION__);
    if (mJobManager.enqueueJob(jobData)) {
        std::unique_lock<std::mutex> lock(mWorkMutex);
        mPending = true;
        lock.unlock();
        mWorkCond.notify_one();
        return true;
    }
    else {
        ALOGE("%s: enqueue job failed!", __FUNCTION__);
    }
    ALOGV("-[%s]", __FUNCTION__);
    return false;
}

size_t ThreadManager::getJobCount() {
   return mJobManager.getJobCount();
}

status_t ThreadManager::startThreads() {
    status_t res = OK;
    for (uint32_t i = 0; i < THRAED_MAX; i++) {
        res = mThreads[i]->run(mName.c_str());
        if (res != OK) {
            ALOGE("%s:run thread %s failed, errno %d",
                __FUNCTION__, mThreads[i]->getThreadName().c_str(), res);
            return res;
        }
        ALOGI("%s:run thread %s successed", __FUNCTION__,
            mThreads[i]->getThreadName().c_str());
    }
    return res;
}

void ThreadManager::stopThreads() {
    ALOGV("+[%s]", __FUNCTION__);
    flush();
    // the flag of parent is not actived 
    mRunning = false;
    // release all blocked threads
    mWorkCond.notify_all();
    for (uint32_t i = 0;i < THRAED_MAX;i++) {
        if (mThreads[i]) {
            mThreads[i]->stop();
        }
    }
    ALOGV("-[%s]", __FUNCTION__);
}

void ThreadManager::flush() {
    mJobManager.flushJob();
}

void ThreadManager::doWork(pid_t tid, std::string threadName) {
    std::unique_lock<std::mutex> lock(mWorkMutex);
    while (!mPending && mRunning) {
        mWorkCond.wait(lock);
    }
    lock.unlock();

    if (mRunning) {
        RuntimeJob* currentJob = mJobManager.dequeueJob();
        if (NULL != currentJob) {

            if (NULL != currentJob->pFuncAddr) {
                currentJob->pFuncAddr(currentJob->pData);
            }

            if (NULL != currentJob->pCb) {
                currentJob->pCb(currentJob->pData);
            }
        }
        else {
            ALOGI("%s:thread id %d name %s currentJob is null!", __FUNCTION__, tid, threadName.c_str());
        }

        mJobManager.releaseRuntimeJob(currentJob);

        lock.lock();
        if (!mJobManager.checkJobsOnHold()) {
            mPending = false;
        }
        lock.unlock();
    }
}

ThreadManager::ThreadFunc::~ThreadFunc() {
    ALOGV("[%s]", __FUNCTION__);
}

void ThreadManager::ThreadFunc::stop() {
//    status_t res = requestExitAndWait();
//    if (res != OK)
//    {
//        ALOGE("[%s] Unable to stop %s thread: %d", __FUNCTION__, mName.c_str(), res);
//    }
    ALOGV("+[%s]", __FUNCTION__);
    requestExit();
    join();
    ALOGV("-[%s]", __FUNCTION__);
}

bool ThreadManager::ThreadFunc::threadLoop() {
    auto parent = mParent.promote();
    // thread dowork only when parent is alived 
    if (parent->isRunning()) {
        parent->doWork(getTid(), mName);
    }
    else {
        return false;
    }
    return true;
}

}// namespace android

为了读者能更好的理解,我将调用关系列出:

三、线程池介绍

1)主要实现有下面两个类:

class JobManager:负责管理注册到线程池的job,以及等待执行队列中的job;

class ThreadManager:负责管理线程的初始化,启动以及释放。

2)主要api介绍:

// 初始化线程池,主要工作是new出来THRAED_MAX个Thread;

void ThreadManager::initialize(std::string name)

// 注册一个job到jobManager,需要提供所执行函数的地址,callback函数地址,以及优先等级

// 函数会返回一个JobHandle,用于后续提交任务到JobManager

bool ThreadManager::registerNewJob(JobFunc jobFuncAddr, JobCb jobCbAddr,JobPriority priority, JobHandle* handle):

//提交一个job到JobManager,并通知thread去执行

bool ThreadManager::postJob(JobData jobData)

//启动线程池

status_t ThreadManager::startThreads()

//停止线程池

void ThreadManager::stopThreads()

3)使用举例:

// 在需要使用线程池的模块初始化函数中初始化线程池,注册job以及启动线程池

sp<ThreadManager> mThreadManager;
mThreadManager = new ThreadManager();
mThreadManager->initialize("CameraDump");
mThreadManager->registerNewJob(printLog, NULL, JobPriority::Critical, &mHandle);
mThreadManager->startThreads();

// 下面是随便写的一个需要线程执行的函数

static void printLog(void* data) {
   (void*)data;
   ALOGD("%s HAODADA NIU BI", __FUNCTION__);
}

// 在模块处理部分postjob,jobData中的handle为注册job时返回handle,pData为线程执行函数时传入的参数

JobData jobData = {
    .handle = mHandle,
    .pData = (void*)dumpData,
};

if (mThreadManager) {
    if (!mThreadManager->postJob(jobData)) {
         ALOGE("%s: post job failed!", __FUNCTION__);
    }
}

// 在模块结束时需要停止线程池,并释放实例

mThreadManager->stopThreads();
mThreadManager.clear();

四、结语

上述过程如果觉得麻烦,还可以再简单点,

// Describes the functions to be executed and their priorities
struct JobEntry {
    JobFunc pFuncAddr;
    JobPriority priority;
    JobCb pCb;
};
struct JobEntrySlot {
    JobEntry pJobEntry;
    bool used = false;
};
std::list<JobEntrySlot> mJobSlot;

在直接registerNewJob到list中

bool JobManager::registerNewJob(JobEntry* jobEntry) {
    uint32_t idx;
	if(mJobSlot.size() < JOB_ENTRY_MAX_SIZE) {
		for (idx = 0; idx < mJobSlot.size(); idx++) {
			if (!mJobSlot[idx].used) {
				JobEntry mJobEntry;
				mJobEntry.pFuncAddr = jobentry->jobFuncAddr;
				mJobEntry.priority = jobentry->priority;
				mJobEntry.pCb= jobentry->jobCbAddr;
				mJobSlot[idx].pJobEntry = mJobEntry;
				mJobSlot[idx].used = true;
				break;
			}
		}
	} else {
        ALOGE("%s: register job failed due to the number of job exceeds %d",
            __FUNCTION__, JOB_ENTRY_MAX_SIZE);
        return false;
    }
    return true;
}

接下来在dowork的时候就可以直接从任务队列中取出相应的job,

RuntimeJob* JobManager::dequeueJob() {;
    std::unique_lock<std::mutex> lock(mJobLock);
    if (!mJobSlot.empty()) {
        JobEntrySlot* jobEntrySlot = mJobSlot.front();
		mJobSlot.pop_front();
		JobEntry* jobEntry = jobEntrySlot->pJobEntry
        if (NULL != jobEntry) {
            RuntimeJob* currentJob = (RuntimeJob*)malloc(sizeof(RuntimeJob));
            currentJob->pFuncAddr = jobEntry->pFuncAddr;
            currentJob->pCb = jobEntry->pCb;
            currentJob->pData = jobData.pData;
            return currentJob;
        }
    }
    lock.unlock();
    return NULL;
}

整个流程也就会非常的简单:

①封装JobEntry;

②register->enque job;

③dowork->deque job。

有兴趣的可以自己实现一下。

该通用线程池是以高通camx-chi架构中camxthreadmanager为模板编写的,路径为:\vendor\qcom\proprietary\camx\src\utils\camxthreadmanager*.cpp,可以去看相关的实现。本文的线程池是一个简单版本,有兴趣的可以添加一下,比如添加优先级:为job添加一个优先级属性,然后在postjob时根据该属性来放到对应的属性队列中,在dowork的时候先遍历高优先级的job queue......后续有需求的时候再来完善,Bye~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值