[ネティ開始]7EventLoopとスレッドモデル


7.EventLoopとスレッドモデル


簡単に言えば、スレッドモデルは、オペレーティングシステム、プログラミング言語、フレームワーク、またはアプリケーションのコンテキストでスレッド管理の主な側面を定義することを意味する.スレッドの生成方法とタイミングはアプリケーションコードの実行に重要な影響を及ぼし,開発者は各種モデルの長所と短所を理解しなければならない.これは,スレッドモデルを直接選択する場合だけでなく,言語やフレームワークを導入しながら適用可能なスレッドモデルを間接的に導入する場合でもある.

7.1スレッドモデルの概要


現在ではマルチコアまたはCPUのシステムが一般的に使用されているため、最新のアプリケーションでは、システムリソースを効率的に利用するために複雑なマルチスレッド技術が一般的に使用されています.Javaベンチャー企業のマルチスレッドシステムは、コンカレントワークユニットを実行する際に、必要に応じて新しいスレッドを作成して起動する基礎であるため、負荷が深刻な場合、パフォーマンスが大幅に低下します.幸いなことにjava 5はExecutor APIを導入し、Threadキャッシュと再利用によってパフォーマンスを大幅に改善するスレッドプールをサポートしています.
基本的なネジ解除モードは以下の通りです.
  • 要求されたタスク(実行可能なインプリメンテーション)を実行するには、プールの使用可能なリストからThreadを選択します.
  • 操作が完了すると、Threadはリストに戻り、繰り返し使用できます.
  • スレッドを解放および再使用する方法は、タスクによってスレッドを作成および削除する方法よりも明らかに優れていますが、コンテキスト切替コストは完全に消えていません.このコストはスレッド数の増加に伴って明らかであり,負荷が深刻な場合に深刻な問題となる.さらに、アプリケーションの同期性の要件や全体的な複雑さのため、プロジェクトライフサイクルでスレッドに関連する他の問題が発生する可能性があります.

    7.2 EventLoopインタフェース


    接続のライフサイクル中に発生するイベントの処理を実行することは、ネットワークフレームワークの基本機能です.これを表すプログラミング構造をイベントループ(event loop)と呼び,ネティでもioである.netty.channel.EventLoopインタフェースに適用します.
    ネティのEventLoopは同期とネットワークの2つの基本APIを採用している.
    1. io.netty.util.そしてJDKパッケージのjavaを発注します.util.コンカレントベースのスレッドエフェクタを提供します.
    2. io.netty.channelパケット内のクラスは、これらのAPIを拡張してchannelイベントおよびインタフェースを実行する.
    このモデルでは、EventLoopは変更されていないThreadとして移動し、EventLoopインプリメンテーションに直接タスクをコミットし、すぐに実行するか、計画的に実行することができます.構成および使用可能なカーネルによっては、リソース使用率を最適化するために複数のEventLoopが作成され、複数のチャネルに単一のEventLoopが割り当てられてサービスが提供される場合があります.
    NettyのEventLoopは、変更されていないThreadを1つだけ移動します.タスク(RunableまたはCallable)をEventLoopインプリメンテーションに直接送信し、すぐに実行または計画することができます.構成および使用可能なカーネルによっては、リソース使用率を最適化するために複数のEventLoopが作成される場合もあれば、複数のチャネルに1つのEventLoopが割り当てられてサービスが提供される場合もあります.
    ネティのEventLoopは計画実行サービスを拡張し,parent()と呼ばれる方法を定義しただけである.このメソッドは、現在のEventLoopインプリメンテーションインスタンスが属するEventLoopGroupの参照を返すために使用されます.
    public interface EventGroup extends EventExecutor, EventLoopGroup {
        @Override
        EventLoopGroup parent();
    }
  • Netie 4のすべてのI/O動作およびイベントは、EventLoopに割り当てられたThreadによって処理される.
  • 以前のリリース・モードでは、I/Oスレッドで実行されるのはインバウンド・イベントのみであり、すべてのアウトバウンド・イベントはI/Oスレッドまたは他のスレッド呼び出しスレッドによって処理される.
  • 7.3タスクのスケジュール


    タスクの実行を遅らせたり、定期的にタスクを実行したりする必要がある場合があります.たとえば、クライアントが5分間接続した後、実行するタスクを登録したい場合があります.これらの動作の一般的な用途は、接続が依然として有効であることを確認する心拍信号を遠隔ピアツーピアネットワークに送信することである.応答がなければ、チャンネルを閉じることができます.

    7.3.1 JDKスケジューリングAPI


    JAva 5以前のタスクスケジューリングは、標準スレッドと同じ制限を持つバックグラウンドThreadのjavaを使用していた.util.Timerで提供します.JDKは、サービスインタフェースの実行計画を定義するjavaです.util.提供して発注した.
    java.util.concurrent.Executors
    newScheduledThreadPool(int corePoolSize,ThreadFactory ThreadFactory)コマンドの計画を示す遅延時間後または定期的な実行方法を作成します.CorePoolSizeパラメータはスレッド数を計算するために使用されます.遅延時間後または定期的にnewSingleThreadFactoryコマンドを実行できる計画ThreadExecutorサービスを作成します.スレッドを使用して計画タスクを実行します.
    サービスの実行を計画するタスク
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(10):
    
    ScheduledFactory<?> future = executor.schedule(
        new Runnable() {
            @Override
            public void run()
            {
                System.out.println("60 seconds later);
            }
        }, 60, TimeUnit.SECONDS);
    )
    executor.shutdown();
    計画実行サービスは使いやすいが、負荷の場合はパフォーマンスが低下する可能性がある.

    7.3.2 EventLoopを使用してタスクをスケジュールする


    「計画実行サービス」の実装には制限があります.たとえば、プール管理タスクの一部として追加のスレッドが作成されるため、多くのタスクが計画されている場合、ボトルネックになる可能性があります.
    EventLoop計画タスクの使用
    Channel ch = ...;
    ScheduledFuture<?> future = ch.eventLoop().schedule(
        new Runnable() {
            @Override
            public void run()
            {
                System.out.println("60 seconds later");
            }
        }, 60, TimeUnit.SECONDS
    );
    EventLoopを使用した重複タスクのスケジュール
    Channel ch = ...
    ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(
        new Runnable() {
            @Override
            public void run()
            {
                System.out.println("Run every 60 seconds");
            }
        }, 60, 60, TimeUnit.Seconds
    );
    ScheduledFutureを使用した操作のキャンセル
    ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(...);
    boolean mayInterruptIfRunning = false;
    future.cancel(mayInterruptIfRunngin);

    7.4実施の詳細


    7.4.1スレッド管理


    組み込みモードは、現在実行されているThreadのID、すなわちThreadが現在のChannelおよびEventLoopに割り当てられているかどうかを決定することによって優れたパフォーマンスを提供します.(EventLoopは、1つのチャネル内のすべてのイベントをライフサイクルで処理します.)
    呼び出しThreadがEventLoopに属する場合、対応するコードブロックが実行されます.そうでない場合、EventLoopは後で予約タスクを実行し、内部キューに入れます.EventLoopは、次のイベントを処理するときにキュー内のアイテムを実行します.ThreadはChannelHandlerに詳しくない場合でも直接Channelと対話することができます.これはその働き方のためです.
    長期実行タスクは実行キューに置くべきではありません.そうしないと、同じスレッドで他のタスクを実行できません.ブロックコールまたは長期実行タスクを実行する必要がある場合は、専用のEventExecutorを使用することが望ましい.

    7.4.2 EventLoopとスレッドの割り当て


    イベントとI/OをサポートするEventLoopは、EventLoopGroupにチャネルに含まれます.EventLoopの作成と割り当ては、転送の実装に依存します.
    ひどうきでんそう
    非同期インプリメンテーションでは少ないEventLoopが使用され、現在のモデルでは複数のチャネルで共有できます.したがって、チャネルごとにThreadを割り当てることなく、最小限のThreadを使用して複数のチャネルをサポートできます.
    EventLoopが割り当てられている場合、チャネルはそのライフサイクルで割り当てられたEventLoopを使用します.したがって,ChannelHandler実装では同期やスレッド安定性の心配は不要である.
    また,EventLoop割当てがThreadLocalに及ぼす影響も理解した.通常、1つのEventLoopは複数のチャネルに使用されるため、ThreadLocalはすべての接続チャネルで同じである.これは、ステータストラッキングなどの機能を実装するのに適していないが、ステータス非ストレージ環境では、複数のチャネルで大規模なオブジェクトまたは高コストのオブジェクトまたはイベントを共有することが非常に有用である可能性があることを意味する.
    ブロッキングでんそう
    各チャネルにEventLoopが割り当てられます.しかし、以前と同様に、各チャネルのI/Oイベントは1つのThreadによって処理される.