小白帶你認識netty(三)之NioEventLoop的線程(或者reactor線程)啓動(三) 原

上一章中,我們看了處理IO事件的過程,今天,我們瞅瞅處理異步任務隊列。

3、處理異步任務隊列

在執行完processSelectedKeys方法後,netty會繼續執行runAllTasks方法,在觀摩這個方法之前,我們瞭解下netty的task。

在初始化NioEventLoop的時候,會實例化兩種task:普通task和scheduledTask,我們分別看看他們:

(1)普通的task

當我們調用NioEventLoop的execute方法,看看是NioEventLoop都做了什麼操作:

無論是不是外部線程調用execute方法,都會執行addTask方法,進入該方法:

進入offerTask方法:

這個taskQueue是什麼?還記的mpsc隊列麼?這個是一個多生產者單消費者安全線程,因此,execute方法會向mpsc隊列中塞入task,該操作是線程安全的。

(2)scheduledTask

在調用NioEventLoop的schedule方法時候,瞅瞅NioEventLoop做了啥?

進入schedule方法:

如果是reactor線程,就執行scheduledTaskQueue().add(task);否則就通過execute執行scheduledTaskQueue().add(task);這兩個有什麼區別呢?

首先,reactor線程內是本線程執行scheduledTaskQueue().add(task);,所以是線程安全。當外部線程調用schedule方法時,就有可能會出現線程安全問題,那麼這裏通過execute方法執行scheduledTaskQueue().add(task);,說明scheduledTaskQueue應該不是線程安全的隊列。爲了驗證我們的猜想,我們進入scheduledTaskQueue方法瞧瞧:

很明顯,這個scheduledTaskQueue是一個非現場安全的隊列,因此證明了我們的觀點。先讓scheduledTaskQueue().add(task);是線程安全,就得在把該操作放入線程安全的mpsc隊列中。

OK,知道了NioEventLoop的兩個任務隊列,我們進入主題,瞧瞧runAllTasks方法。進入該方法:

/**
     * Poll all tasks from the task queue and run them via {@link Runnable#run()} method.  This method stops running
     * the tasks in the task queue and returns if it ran longer than {@code timeoutNanos}.
     */
    protected boolean runAllTasks(long timeoutNanos) {
        fetchFromScheduledTaskQueue();
        Runnable task = pollTask();
        if (task == null) {
            afterRunningAllTasks();
            return false;
        }

        final long deadline = ScheduledFutureTask.nanoTime() + timeoutNanos;
        long runTasks = 0;
        long lastExecutionTime;
        for (;;) {
            safeExecute(task);

            runTasks ++;

            // Check timeout every 64 tasks because nanoTime() is relatively expensive.
            // XXX: Hard-coded value - will make it configurable if it is really a problem.
            if ((runTasks & 0x3F) == 0) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                if (lastExecutionTime >= deadline) {
                    break;
                }
            }

            task = pollTask();
            if (task == null) {
                lastExecutionTime = ScheduledFutureTask.nanoTime();
                break;
            }
        }

        afterRunningAllTasks();
        this.lastExecutionTime = lastExecutionTime;
        return true;
    }

先看一下聚合聚合方法fetchFromScheduledTaskQueue:

進入pollScheduledTask方法:

該方法是從scheduledTaskQueue獲取第一個scheduledTask。再回到fetchFromScheduledTaskQueue:

循環從scheduledTaskQueue中獲取scheduledTask塞入到taskQueue中,此處的處理還是非常小心的,如果塞入失敗後,再將該task放回到scheduledTaskQueue。回到runAllTasks方法中,我們繼續向下看。

進入pooTask方法:

繼續進入pollTaskFrom方法:

從taskQueue中獲取第一個task。回到runAllTasks方法上,繼續向下看:

這一步就是本章核心代碼了。首先調用safeExecute方法,進入該方法摟一眼:

僅僅是執行task的run方法。

執行完safeExecute方法後,就將runTasks自增1。然後每隔0x3F即64個任務就判斷當前時間是否超過了本次reactor任務循環的截至時間,如果超過了就執行break,否則就繼續取task執行。

好了,處理異步任務隊列講完了,總結一下:

首先,netty會將已經到期的定時任務放入到MPSC隊列中,然後循環執行task的run方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章