小白带你认识netty(三)之NioEventLoop的线程(或者reactor线程)启动(三)

上一章中,我们看了处理IO事件的过程,今天,我们瞅瞅处理异步任务队列。

3、处理异步任务队列

236690f43934de0a6bd3e13562ee242aed0.jpg

在执行完processSelectedKeys方法后,netty会继续执行runAllTasks方法,在观摩这个方法之前,我们了解下netty的task。

在初始化NioEventLoop的时候,会实例化两种task:普通task和scheduledTask,我们分别看看他们:

(1)普通的task

当我们调用NioEventLoop的execute方法,看看是NioEventLoop都做了什么操作:

f47f0de6c1c6fae1d3351465c714af4ff29.jpg

无论是不是外部线程调用execute方法,都会执行addTask方法,进入该方法:

4dca4959a52a7af299ef3626766de8d9c5d.jpg

进入offerTask方法:

c5123fcb98039cd93fe42c94e62bc264934.jpg

这个taskQueue是什么?还记的mpsc队列么?这个是一个多生产者单消费者安全线程,因此,execute方法会向mpsc队列中塞入task,该操作是线程安全的。

(2)scheduledTask

在调用NioEventLoop的schedule方法时候,瞅瞅NioEventLoop做了啥?

944b65fe11f9fa983068bb44565cb4ab55b.jpg

进入schedule方法:

5ffb2cc16912c48d6d4a32acf013d7424a6.jpg

如果是reactor线程,就执行scheduledTaskQueue().add(task);否则就通过execute执行scheduledTaskQueue().add(task);这两个有什么区别呢?

首先,reactor线程内是本线程执行scheduledTaskQueue().add(task);,所以是线程安全。当外部线程调用schedule方法时,就有可能会出现线程安全问题,那么这里通过execute方法执行scheduledTaskQueue().add(task);,说明scheduledTaskQueue应该不是线程安全的队列。为了验证我们的猜想,我们进入scheduledTaskQueue方法瞧瞧:

dcc6fb6d88905d2b81e6f213e7e9c66b6b8.jpg

266aa4883389e349ff07800eff78797aa4e.jpg

很明显,这个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:

4d8ac770de22951ee6a09cc1da1a740ad3e.jpg

进入pollScheduledTask方法:

a933534449d47846718d256abfdeb1a6b70.jpg

该方法是从scheduledTaskQueue获取第一个scheduledTask。再回到fetchFromScheduledTaskQueue:

10b6ea756666fa0b7fd3228ae6c8f9146d6.jpg

循环从scheduledTaskQueue中获取scheduledTask塞入到taskQueue中,此处的处理还是非常小心的,如果塞入失败后,再将该task放回到scheduledTaskQueue。回到runAllTasks方法中,我们继续向下看。

437c2a3a8a5566f5ade3f7e3fe7c96442ad.jpg

进入pooTask方法:

6ed5cd13725170c694aaa1ca6f009a3ff60.jpg

继续进入pollTaskFrom方法:

ede7dc25010b80dcf799a689beb8aae9312.jpg

从taskQueue中获取第一个task。回到runAllTasks方法上,继续向下看:

62246c37e0ef0a504c769d1d89db46f8564.jpg

这一步就是本章核心代码了。首先调用safeExecute方法,进入该方法搂一眼:

ed8a4996c8cf84103206abcbd26a804fea7.jpg

仅仅是执行task的run方法。

执行完safeExecute方法后,就将runTasks自增1。然后每隔0x3F即64个任务就判断当前时间是否超过了本次reactor任务循环的截至时间,如果超过了就执行break,否则就继续取task执行。

好了,处理异步任务队列讲完了,总结一下:

首先,netty会将已经到期的定时任务放入到MPSC队列中,然后循环执行task的run方法。

转载于:https://my.oschina.net/littlestyle/blog/2963595

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值