시동 3.NioEventLoop 및 실행

NioEventLoop 시작 및 실행

NioEventLoop 시작

현재의 thread가 NioEventLoop 스레드인지 여부를 결정하기 위해, 등록 및 바인딩 할 때 코드 서버가 시작, 우리는 그물코를 참조하십시오. 그렇지 않은 경우, 이러한 작업을 완료하는 데에 던져 작업 EventExecutor로 포장됩니다.

// 调用SingleThreadEventExecutor对象的execute方法
eventLoop.execute(() -> register0(promise));

// SingleThreadEventExecutor对象的execute方法
@Override
public void execute(Runnable task) {
    boolean inEventLoop = inEventLoop();
    addTask(task);
    if (!inEventLoop) {
        startThread();
    }

    if (!addTaskWakesUp && wakesUpForTask(task)) {
        wakeup(inEventLoop);
    }
}

실행 메소드에서 다시이 NioEventLoop 스레드 아니더라도 실행 startThread 방식인지 여부를 결정한다. 스레드의 상태를 수정하여 startThread CAS 방법은 성공적으로 입력 doStartThread 방법 후 시작됩니다. NioEventLoop 실행을 만들 때이 방법은 작업, 언급 집행자 세트를 랩합니다. 기본적으로는 ThreadPerTaskExecutor은, 그러므로,이 작업을 수행 할 수있는 새로운 스레드를 시작합니다. 논리 패키지의 주요 임무는 세 가지이다 :

  1. 바인딩 현재 스레드 NioEventLoop;
  2. 현재 오랜 시간의 마지막 실행 업데이트 할 때 - 작업 시작 시간을;
  3. 수행 NioEventLoop 실행 방법;
private void doStartThread() {
    executor.execute(() -> {
        thread = Thread.currentThread();
        updateLastExecutionTime();
        SingleThreadEventExecutor.this.run();
    });
}

이 시점에서, NioEvnetLoop 시작했다.

NioEventLoop 실행

NioEventLoop 시작할 때, 우리는 실행 방법 SingleThreadEventExecutor을 실행하기 시작합니다. 이 방법은 무한 루프, 그것은 세 단계로 나누어 질 수있다

  1. 준비 IO 이벤트 폴링 채널
  2. 폴링 이벤트의 IO 처리
  3. 일반 작업을 포함한 모든 작업을 처리

폴링 이벤트

다음과 같이 전체 폴링 프로세스 IO 이벤트입니다

switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
    case SelectStrategy.CONTINUE:
        continue;
    case SelectStrategy.BUSY_WAIT:
    case SelectStrategy.SELECT:
        select(wakenUp.getAndSet(false));
        if (wakenUp.get()) {
            selector.wakeup();
        }
    default:
}

사이클의 시작 부분에서, SelectStrategy.SELECT를 입력 즉 호출 그렇지 않으면에 selectNow ()를 호출, 아무 작업이 없을 경우 작업이 있는지 여부를 확인 첫째, 선택 전략의 기본 정책을 선택하는 선택 정책을 호출 선택 (wakeUp.getAndSet (거짓) ).

에 selectNow () 메소드

int selectNow() throws IOException {
    try {
        return selector.selectNow();
    } finally {
        if (wakenUp.get()) {
            selector.wakeup();
        }
    }
}

에 selectNow 방법의 nioEventLoop 선택기 개체에 selectNow 방법 개최 호출합니다. selector.select 방법이 차단됩니다 동안이 폴링 방법 후,이 경우에도 이벤트는, 즉시 반환하지 않습니다. 마지막으로 작업이 방해가 현재 즉시 반환됩니다 선택한 다음 시간을 선택하지 않을 경우,이 방법은 장애물을 만드는 방법을 선택 웨이크, wakenUp 필드에 해당하는 경우, 메소드가 selector.wakeup 한 번 호출되어 있는지 확인합니다.

선택 (부울 oldWakenUp) 方法

첫째, 우리는 wakeup.getAndSet (false)를 참조를 참조하십시오. 간단히에 역할 웨이크 나중에 분석은 그 역할이 선택 웨이크의 차단을 제어하는 ​​것입니다 언급. 자세한 코드에 따라

private void select(boolean oldWakenUp) throws IOException {
Selector selector = this.selector;
int selectCnt = 0;
// 步骤1
long currentTimeNanos = System.nanoTime();
long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
long normalizedDeadlineNanos = selectDeadLineNanos - initialNanoTime();
if (nextWakeupTime != normalizedDeadlineNanos) {
    nextWakeupTime = normalizedDeadlineNanos;
}
for (;;) {
    // 步骤2
    long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
    if (timeoutMillis <= 0) {
        if (selectCnt == 0) {
            selector.selectNow();
            selectCnt = 1;
        }
        break;
    }
    // 步骤3
    if (hasTasks() && wakenUp.compareAndSet(false, true)) {
        selector.selectNow();
        selectCnt = 1;
        break;
    }
    // 步骤4
    int selectedKeys = selector.select(timeoutMillis);
    selectCnt++;
    if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
        break;
    }

    // 步骤5
    if (Thread.interrupted()) {
        selectCnt = 1;
        break;
    }
    // 步骤6
    long time = System.nanoTime();
    if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
        selectCnt = 1;
    } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
            selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
        selector = selectRebuildSelector(selectCnt);
        selectCnt = 1;
        break;
    }
    currentTimeNanos = time;
}

긴 코드는 여섯 단계로 나눌 수 있습니다

  1. 1 단계 계산 나노초 수준, 현재 시간 ⑴ 정확한 시간의 복수 ⑵select 마감, 여기에 근거가 계산됩니다 차단 여부를 작업의 타이밍 경우 시간의 타이밍 작업, 최근의 타이밍을 이륙하는 작업 시간이 작업 예약 된 작업을 예약하거나하지 아니하는 경우 시간, 다음 1 초 걸릴 없다 ⑶ 정규화 다음 웨이크 업 시간과 마감

2 단계와, 무한 루프로 시작 2. 블로킹 타임 아웃을 계산 라운딩. 타임 아웃 시간이 0보다 작은 0 인 경우 복귀에 selectNow 후 한 번 수행, 설문 조사의 수는 비어 있습니다. 3. 폴링 작업, 참 거짓에서 웨이크 업 세트 및이에 selectNow을 수행하는 경우에는 작업이, 없다를 결정하기 전에. 때문에 그렇지 않으면이 작업의 실행 선택 지연을 깨워하지 않습니다. 구현이주기의 종료 후. 4. 차단을 선택합니다. 차단 한 후, 다음 조건 중 하나가,이주기의 끝 때 발생합니다, ⑵는 (부울 웨이크 업)을 선택 입력하기 전에 oldWakeup에 해당하는 매개 변수, 즉의 액션 웨이크 전의된다 ⑴ 폴링 IO 이벤트에 ⑶ 일어나는 현재의 필요, ⑷ 작업 큐, 외부 스레드가 추가 될 수있다; 사용자는 방법을 깨워 웨이크 활성 통화를 할 수있다 ⑸ 시간 초과 작업은 스레드가 깨진 경우, 빈 폴링 시간은 1로의 끝을 설정 5. 만료가를 현재 시간 및 계산주기의 시간 입력 방법은, 시간 차단 시간을 선택할지 여부를 결정 - TimeUnit.MILLISECONDS.toNanos (timeoutMillis)> = currentTimeNanos 확립있다 시간 currentTimeNanos을> = timeoutMillis는 실행 지시를 선택 시간의 부족, 더 임계 값, 재건 선택기보다, 빈 폴링 JDK를 피하기 위해 여론 조사의 숫자, 누적 주파수가 비어있는 임계 값보다 큰 폴링 계산 (기본 임계 값 SELECTOR_AUTO_REBUILD_THRESHOLD 512), 비어, 비어 설문 조사를 트리거 할 수 버그. 반대로, 그것은 빈 폴링의 총 수는 1로 설정되고, 올바른 선택주기의 끝을 가지고 있었다.

빈 폴링 버그를 피하십시오

버그 빈 폴링을 피하기 위해 사실의 그물코의 방법으로도 매우 영리하다 새로운 선택을 통해, 그리고 기존의 선택에 복사 과거 키 attchment

private Selector selectRebuildSelector(int selectCnt) throws IOException {
    rebuildSelector();
    Selector selector = this.select
    // Select again to populate selectedKeys.
    selector.selectNow();
    return selector;
}

private void rebuildSelector0() {
    final Selector oldSelector = selector;
    final SelectorTuple newSelectorTuple
    newSelectorTuple = openSelector();
    // 将老selector的key和attchment传递给新selector
    for (SelectionKey key : oldSelector.keys()) {
        Object a = key.attachment();
        int interestOps = key.interestOps();
        key.cancel();
        SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);
        if (a instanceof AbstractNioChannel) {
            // Update SelectionKey
            ((AbstractNioChannel) a).selectionKey = newKey;
        }
    selector = newSelectorTuple.selector;
    unwrappedSelector = newSelectorTuple.unwrappedSelector;
    // 关闭老selector
    oldSelector.close();
    }
}

충분히 상세 코드, 설명하기 위해 더 많은 비용을 지불하지 않습니다

(wakeup.getAndSet (거짓))가 구현 후, 코드 그래서 몇 줄이 있습니다 선택 ①

if (wakenUp.get()) {
    selector.wakeup();
}

우리는 이전의 웨이크 업 역할은 선택 웨이크의 차단을 제어하는 ​​것입니다 언급했다. 저 아래에서 자세히 이것에 대해 얘기하자. 방법은 추가 작업 후 행동에 모닝콜이 실행 위, SingleThreadEventExecutor 객체를하는 불러 오기, nioEventLoop는 웨이크 업 다음 다시

protected void wakeup(boolean inEventLoop) {
if (!inEventLoop && wakenUp.compareAndSet(false, true)) {
        selector.wakeup();
    }
}

이 외부 스레드, selector.wakeup () 선택 작업의 차단 즉시 임무가 추가 된이 시점에 일어나 것을 여기에 두 심사 위원 ,! InEventLoop이었다. 여파 선택 시간에 태스크를 태스크를 추가 처리하는 동안이 방법은 그물코의 해석에 따른 외부 스레드를 허용 wakeup.compareAndSet은 (TRUE, FALSE)는 항상 다수의 태스크를 selector.wakeup을 줄이고 그래서 동안 selector.wakeup 전이라고 성능 소비.

그 초기 웨이크가 true로 설정되어 있으므로 ① 언급 한 추가 코멘트 코드는 두 개의 경쟁 조건이 있습니다.

  1. 선택기는 wakeup.set (거짓) 및 selector.select (시간 초과) 사이에 깨어됩니다. 여기에는 4 단계에 앞서 일어난다.
  2. 선택기는 () 사이 경우 selector.select에서 깨어 경우 (wakeup.get ()). 이 4 단계 이후에 발생한다. 첫 번째 경우, 다음 시간 selector.select (시간 초과) (참고 :라고 선택 1)는 즉시 일어나 것입니다. 인해 진실 wakeup.compareAndSet (TRUE, FALSE)로 웨이크 업 기간 경우 통화 selector.wakeup에 실패 결과 실패 후 (시간 단계로부터 다음 selector.select 4 (타임 아웃) (참고는 : 선택 2를 언급 됨))를 첨가 하였다 작업이 다음 시간 제한을 선택한 다음 시간까지 기다려야 작업이 처리 할 수 있습니다. 문의가 작업을 완료 그래서 후 발견 웨이크 업에 해당하는 경우, 다음 selector.wakup 번 ()를 호출합니다. 그러나주의 깊은 독자는 wakenUp.compareAndSet 그렇게 (거짓, 진실, 3 단계에서 몇 가지 조건에서, 그물코가 태스크가 있는지 여부를 확인 hasTask 큐 작업을 호출하고, 선택 방법에 들어가기 전에, false로 설정 웨이크 점에 유의한다 ) 성공, 따라서 선택 2 처리 작업 전에 제한 시간을 기다리지 않고,에 selectNow를 호출합니다. 두 번째 경우, 선택 2 즉시 반환, 아무 문제. 이 코드 그 의미는 무엇인가? 사실 나는 개인적으로이 코드는 저자의 이유로, 레거시 코드에 속한다는 것을 생각 52im AbstractNioSelector 클래스에 netty3에 코드 netty3를 찾아 웨이크 거짓을 설정 한 후, 직접 selector.select (시간 제한)로 전화하십시오. 당시 듯, 이것은 확실히 솔루션입니다.

여기에, NioEventLoop가 시작을 완료하고의 SelectionKey를 확인, 다음 단계의 SelectionKey를 처리하는 것입니다. 그래서 ···· 계속하려면

추천

출처www.cnblogs.com/spiritsx/p/11945426.html