安卓线程学习四 之 线程优先级和调度

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rambomatrix/article/details/81476145

一 问题思考

安卓开发,如果想设置线程优先级有两种方法:

1 Android sdk也提供一个设置线程优先级的方法

2 Thread.java里面提供了设置线程优先级的方法

这两个方法有什么区别,应该选择使用哪一个呢?

二 线程优先级的原理

2.1. android.os.process.java设置线程优先级源码分析

/**
     * Set the priority of the calling thread, based on Linux priorities.  See
     * {@link #setThreadPriority(int, int)} for more information.
     * 
     * @param priority A Linux priority level, from -20 for highest scheduling
     * priority to 19 for lowest scheduling priority.
     * 
     * @throws IllegalArgumentException Throws IllegalArgumentException if
     * <var>tid</var> does not exist.
     * @throws SecurityException Throws SecurityException if your process does
     * not have permission to modify the given thread, or to use the given
     * priority.
     * 
     * @see #setThreadPriority(int, int)
     */
    public static final native void setThreadPriority(int priority)
            throws IllegalArgumentException, SecurityException;

注释的意思大致是这里设置优先级是基于linux线程优先级的实现。这是一个jni方法,对应实现文件在android_util_process.cpp文件:

tatic const JNINativeMethod methods[] = {
    {"getUidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
    {"getGidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
    {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
    {"setThreadScheduler",  "(III)V", (void*)android_os_Process_setThreadScheduler},
    {"setCanSelfBackground", "(Z)V", (void*)android_os_Process_setCanSelfBackground},
    {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority}
    ...

对应实现方法是android_os_Process_setThreadPriority

进入方法实现:

void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                              jint pid, jint pri)
{
#if GUARD_THREAD_PRIORITY
    // if we're putting the current thread into the background, check the TLS
    // to make sure this thread isn't guarded.  If it is, raise an exception.
    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
        if (pid == gettid()) {
            void* bgOk = pthread_getspecific(gBgKey);
            if (bgOk == ((void*)0xbaad)) {
                ALOGE("Thread marked fg-only put self in background!");
                jniThrowException(env, "java/lang/SecurityException", "May not put this thread into background");
                return;
            }
        }
    }
#endif

    int rc = androidSetThreadPriority(pid, pri);
    if (rc != 0) {
        if (rc == INVALID_OPERATION) {
            signalExceptionForPriorityError(env, errno, pid);
        } else {
            signalExceptionForGroupError(env, errno, pid);
        }
    }

    //ALOGI("Setting priority of %" PRId32 ": %" PRId32 ", getpriority returns %d\n",
    //     pid, pri, getpriority(PRIO_PROCESS, pid));
}

继续跟踪androidSetThreadPriority(pid, pri),这个实现在
/frameworks/native/libs/utils/Threads.cpp中

int androidSetThreadPriority(pid_t tid, int pri)
{
    int rc = 0;
    #if defined(HAVE_PTHREADS)
    int lasterr = 0;
    pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
    if (gDoSchedulingGroup) {
        // set_sched_policy does not support tid == 0
        int policy_tid;
        if (tid == 0) {
            policy_tid = androidGetTid();
        } else {
            policy_tid = tid;
        }
        //设置调度策略:1.如果priority大于等于BACKGROUND,则设置为BACKGROUND类型的调度策略
        //2.如果priority小于BACKGROUND,且当线程为BACKGROUND类型,则设置为FOREGROUND类型
        if (pri >= ANDROID_PRIORITY_BACKGROUND) {
            rc = set_sched_policy(policy_tid, SP_BACKGROUND);
        } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) {
            rc = set_sched_policy(policy_tid, SP_FOREGROUND);
        }
    }

    if (rc) {
        lasterr = errno;
    }
    //设置优先级
    if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
        rc = INVALID_OPERATION;
    } else {
        errno = lasterr;
    }
#endif

    return rc;
}

1. 设置调度策略set_sched_policy
对应代码目录./AndroidSdk/iot_speech_board/system/core/libcutils/sched_policy.c

int set_sched_policy(int tid, SchedPolicy policy)
{
#ifdef HAVE_GETTID
    if (tid == 0) {
        tid = gettid();
    }
#endif
    policy = _policy(policy);
    pthread_once(&the_once, __initialize);

#if POLICY_DEBUG
    char statfile[64];
    char statline[1024];
    char thread_name[255];
    int fd;

    sprintf(statfile, "/proc/%d/stat", tid);
    memset(thread_name, 0, sizeof(thread_name));

    fd = open(statfile, O_RDONLY);
    if (fd >= 0) {
        int rc = read(fd, statline, 1023);
        close(fd);
        statline[rc] = 0;
        char *p = statline;
        char *q;

        for (p = statline; *p != '('; p++);
        p++;
        for (q = p; *q != ')'; q++);

        strncpy(thread_name, p, (q-p));
    }
    switch (policy) {
    case SP_BACKGROUND:
        SLOGD("vvv tid %d (%s)", tid, thread_name);
        break;
    case SP_FOREGROUND:
    case SP_AUDIO_APP:
    case SP_AUDIO_SYS:
        SLOGD("^^^ tid %d (%s)", tid, thread_name);
        break;
    case SP_SYSTEM:
        SLOGD("/// tid %d (%s)", tid, thread_name);
        break;
    default:
        SLOGD("??? tid %d (%s)", tid, thread_name);
        break;
    }
#endif

    if (__sys_supports_schedgroups) {
        if (add_tid_to_cgroup(tid, policy)) {
            if (errno != ESRCH && errno != ENOENT)
                return -errno;
        }
    } else {
        struct sched_param param;

        param.sched_priority = 0;
        sched_setscheduler(tid,
                           (policy == SP_BACKGROUND) ?
                            SCHED_BATCH : SCHED_NORMAL,
                           &param);
    }

    prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid);

    return 0;
}

SP_BACKGROUND对应SCHED_BATCH调度策略;其他对应SCHED_NORMAL调度策略。

<1>SCHED_NORMAL用于普通进程,通过完全公平调度器来处理。
<2>SCHED_BACH也通过完全公平调度器来处理,用于非交互,CPU使用密集的批处理进程,CPU绝不会抢占CF调度器处理的另一个进程,不会干扰交互式进程。 优先级相比SCHED_NORMAL要低。

2. 调用系统api setpriority(PRIO_PROCESS, tid, pri)设置线程优先级。

2.2 Thread.java设置线程优先级源码分析

参考java线程启动流程源码分析可以很快找到java线程设置优先级的方法最终会走到os_linux.cpp的set_native_priority:

OSReturn os::set_native_priority(Thread* thread, int newpri) {
  if ( !UseThreadPriorities || ThreadPriorityPolicy == 0 ) return OS_OK;

  int ret = setpriority(PRIO_PROCESS, thread->osthread()->thread_id(), newpri);
  return (ret == 0) ? OS_OK : OS_ERR;
}

最终是直接调用系统api设置线程优先级:
setpriority(PRIO_PROCESS, thread->osthread()->thread_id(), newpri)

2.3 安卓sdk与jdk设置线程优先级对比

  1. 两者最终都是通过调用linux系统apisetpriority设置线程优先级。
  2. 差别是android.os.Process.java设置线程优先级之前会先调整线程调度策略,这是针对安卓系统的定制特性。

因此,如果在安卓开发过程中,我们应该使用Process.java中提供的接口。

三 总结

本文分析了anroid.os.Process.java和jdk Thread.java设置线程优先级的源码,对比了二者的异同。明确了安卓开发中如果需要设置线程优先级应当使用sdk提供的接口。涉及到的Linux线程调度策略和优先级nice设定没有详细展开,这是linux内核的一个非常重要和庞大的知识点,后面将专项继续学习。

猜你喜欢

转载自blog.csdn.net/rambomatrix/article/details/81476145