一 问题思考
安卓开发,如果想设置线程优先级有两种方法:
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,
¶m);
}
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设置线程优先级对比
- 两者最终都是通过调用linux系统apisetpriority设置线程优先级。
- 差别是android.os.Process.java设置线程优先级之前会先调整线程调度策略,这是针对安卓系统的定制特性。
因此,如果在安卓开发过程中,我们应该使用Process.java中提供的接口。
三 总结
本文分析了anroid.os.Process.java和jdk Thread.java设置线程优先级的源码,对比了二者的异同。明确了安卓开发中如果需要设置线程优先级应当使用sdk提供的接口。涉及到的Linux线程调度策略和优先级nice设定没有详细展开,这是linux内核的一个非常重要和庞大的知识点,后面将专项继续学习。