调度流程中有三个主要的类
关键属性有:
public static int defaultNumberThreads;
public LinkedList allThreads = new LinkedList();//所有线程
public RingQueue waitingThreads = new RingQueue(10);//
protected volatile boolean shutdown = false;
public RingQueue runnableTasks = new RingQueue(100);
其中RingQueue是实现的一个环形队列,runnableTasks用于存储需要调度的task,waitingThreads是在等待的线程。
初始化时会创建WorkerThread,创建的线程个数是CPU可用,并运行
public Scheduler(int numThreads) {
for(int i = 0; i < numThreads; ++i) {
WorkerThread wt = new WorkerThread(this);
this.allThreads.add(wt);
this.addWaitingThread(wt);
wt.start();
}
}
调度时,加入runnableTasks,同时从waitingThreads取出一个workThread,调用其notify,是因为workThread在没有Task时会调用wait()等待Task
public void schedule(Task t) {
WorkerThread wt = null;
synchronized(this) {
assert t.running : "Task " + t + " scheduled even though running is false";
this.runnableTasks.put(t);
}
wt = this.getWaitingThread();
if (wt != null) {
synchronized(wt) {
wt.notify();
}
}
}
另一个重要的函数,也是从runnableTasks中取出task,然后通知合适的调度线程
void loadNextTask(WorkerThread wt) throws ShutdownException {
while(true) {
Task t = null;
WorkerThread prefThread = null;
synchronized(this) {
if (this.shutdown) {
throw new ShutdownException();
}
t = (Task)this.runnableTasks.get();
if (t != null) {
prefThread = t.preferredResumeThread;
if (prefThread != null && prefThread != wt) {
prefThread.addRunnableTask(t);
synchronized(prefThread) {
prefThread.notify();
continue;
}
}
wt.addRunnableTask(t);
}
return;
}
}
}
WorkerThread其实是个线程,线程的名称为KilimWorker-x,x递增,看一下run方法,其核心就是不停的取出task,再执行
public void run() {
try {
while(true) {
Task t = this.getNextTask(this);
this.runningTask = t;
t._runExecute(this);
this.runningTask = null;
}
} catch (ShutdownException var2) {
;
} catch (OutOfMemoryError var3) {
System.err.println("Out of memory");
System.exit(1);
} catch (Throwable var4) {
var4.printStackTrace();
System.err.println(this.runningTask);
}
this.runningTask = null;
}
getNextTask方法从环形队列tasks中取出task,如果task为空,将会执行this.scheduler.loadNextTask,从scheduler中取task,如果也为空,将自身加入schduler的waitingThreads。注意在schduler构造函数中和这里都将workThread加入了waitingThreads,也就是说waitingThreads中每个workThread都存在的两份KilimWorker-x以备调度。
RingQueue tasks = new RingQueue(10);
protected Task getNextTask(WorkerThread workerThread) throws ShutdownException {
Task t = null;
while(true) {
if (this.scheduler.isShutdown()) {
throw new ShutdownException();
}
t = this.getNextTask();
if (t != null) {
break;
}
this.scheduler.loadNextTask(this);
synchronized(this) {
t = (Task)this.tasks.get();
if (t != null) {
break;
}
this.scheduler.addWaitingThread(this);
try {
this.wait();
} catch (InterruptedException var6) {
;
}
}
}
assert t != null : "Returning null task";
return t;
}
public synchronized Task getNextTask() {
return (Task)this.tasks.get();
}
Task类似于Java原生的Thread,需要调用start方法,首先设置Scheduler
public Task start() {
if (this.scheduler == null) {
this.setScheduler(Scheduler.getDefaultScheduler());
}
this.resume();
return this;
}
resume函数主要是调用Scheduler对task进行调度。
public boolean resume() {
if (this.scheduler == null) {
return false;
} else {
boolean doSchedule = false;
synchronized(this) {
if (this.done || this.running) {
return false;
}
doSchedule = true;
this.running = true;
}
if (doSchedule) {
this.scheduler.schedule(this);
}
return doSchedule;
}
}
至此一整套调度流程也就结束了,基本还是比较清晰,其核心就是多个线程取task执行,但是用个BlockingQueue就能简单的做出getNextTask方法的功能。Jungle-Server-Core中对Kilim调度的Scheduler、WorkThread和Task进行了包装,可以使用BlockingQueue进行调度,只是默认不开启。
在Kilim中,对只有抛出了Pausable的方法进行字节码优化,没有优化的代码将会阻塞协程,严格讲应该是阻塞了WorkThread,使得协程退化成了线程。那这样的话,没有正确使用协程的程序的性能也应该优于同线程个数的多线程程序,最起码是持平。