Kilim调度流程

调度流程中有三个主要的类

  • Scheduler-调度器
  • WorkThread-调度运行承载线程
  • Task-类似于Thread,协程的体现者

主题流程如下:
Kilim调度流程_第1张图片

Scheduler

关键属性有:

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

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

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,使得协程退化成了线程。那这样的话,没有正确使用协程的程序的性能也应该优于同线程个数的多线程程序,最起码是持平。

  • 但是在之前的一次压测中,由于使用了不正确的网络I/O API,阻塞线程,但是其请求量只能压到8k/min,基本等同于同样线程数的多线程程序。
  • 在前面已经说过,waitingThreads队列中存有两份KilimWorker-x,例如共有KilimWorker-1和KilimWorker-2,那么waitingThreads中的排列顺序可能是1,1,2,2或者1,2,1,2。以1,1,2,2为例,waitingThreads取出1 执行task1后,下次还会在取出1,再执行task2,那么第二个task将等待第一个task执行完毕后再执行。如果task1是较长时间的阻塞的话,那么task2可能在长时间内无法执行。

你可能感兴趣的:(Java,腾讯工作,kilim,scheduler,调度,源码)