令和3年度 春期 応用情報技術者試験 午後 問題 問8 リアクタパターンを用いたノンブロッキングI/Oのソフトウェア設計

学習ガイド

スマートフォン向けクーポン券発行システムのWebアプリを開発する情報システム開発問題です。大量の同時アクセスに対応するため、従来のブロッキングI/OからノンブロッキングI/O方式への移行と、リアクタパターンを用いた非同期処理のソフトウェア設計が問われます。I/O完了待ちによるCPUの無駄をなくす仕組みを理解し、一連の順次処理をイベント駆動型のハンドラ処理にどう分割し、どのような順序で実行させるかを整理します。

この記事で押さえる論点

  • ブロッキングI/OとノンブロッキングI/Oの違いと、それぞれの処理方式の特徴を説明できる
  • リアクタパターンの構成要素(リアクタ、デマルチプレクサ、ハンドラ)の役割を述べられる
  • 一連の順次処理を、I/O待ちを契機として複数のハンドラに分割して設計できる

問題本文

問8 クーポン券発行システムの設計に関する次の記述を読んで,設問1〜3に答えよ。

X社は,全国に約400店のファミリーレストランを展開している。X社の会員向けWebサイトでは,割引料金で商品を注文できるクーポン券を発行しており,会員数は1,000万人を超える。このたび,会員の利便性の向上や店舗での注文受付業務の効率向上のために,会員のスマートフォン宛てにクーポン券を発行することになった。

スマートフォン宛てにクーポン券を発行する新しいシステム(以下,新システムという)は,スマートフォン向けアプリケーションソフトウェア(以下,スマホアプリという)とサーバ側のWebアプリケーションソフトウェア(以下,Webアプリという)から構成され,Webアプリの開発は情報システム部門のY君が担当することになった。

〔新システムの利用イメージ〕

X社の会員は,事前に自分のスマートフォンにX社のスマホアプリをダウンロードし,インストールしておく。会員がクーポン券を利用する際は,スマホアプリに会員IDとパスワードを入力してログインする。ログインが完了すると,おすすめ商品と利用可能なクーポン券の一覧が表示される。会員が利用したいクーポン券を選択すると,QRコードを含むクーポン券画面が表示される。X社店舗の注文スタッフがQRコードを注文受付端末で読み取ると,割引料金での注文ができる。

〔Webアプリの処理方式の調査〕

Y君がWebアプリの実現方式を検討したところ,X社のWebサイトで利用しているブロッキングI/O型のWebサーバソフトウェア(以下,サーバソフトという)では,スマホアプリからの同時アクセス数が増えると対応できないことが分かった。

ブロッキングI/O型のサーバソフトでは,ネットワークアクセスやファイルアクセスなどのI/O処理を行う場合,CPUは低速なI/O処理の完了を待って次の処理を実行する。例えば,表1に示す,QRコードを作成するために必要なWebアプリの処理(以下,QRコード作成処理という)の場合,全体の処理時間のa%がI/O処理の完了待ち時間となる。

表1 QRコード作成処理
図の説明テキスト

表1 QRコード作成処理

処理番号 処理内容 開始条件 処理時間(ミリ秒) 処理区分
1 会員のログイン状態を確認 なし 0.02 CPU処理
2 スマホアプリからクーポン券番号を取得 処理1の完了 10 I/O処理
3 クーポン券番号から QR コードの画像データをメモリに作成 処理2の完了 0.06 CPU処理
4 QR コードの画像データを画像ファイルに書き出し 処理3の完了 3 I/O処理
5 クーポン券番号を発行履歴としてデータベースに書き込み 処理2の完了 15 I/O処理
6 スマホアプリに QR コードの画像ファイルを返信 処理4の完了 5 I/O処理
7 QR コードの画像ファイルを削除 処理6の完了 2 I/O処理

このため,ブロッキングI/O型のサーバソフトでは,複数のスマホアプリにサービスを提供するために,プロセスやスレッドを複数生成している。しかし,プロセスやスレッドの数が増えると,プロセスやスレッドの切替え処理であるbスイッチがボトルネックとなり,CPUやメモリを追加してもスマホアプリからの同時アクセスへの対応は困難となる。

そこでY君は,多数のスマホアプリからのアクセスを効率よく処理できるノンブロッキングI/O型のサーバソフトの利用を検討した。ノンブロッキングI/O型のサーバソフトでは,一つのプロセスやスレッドの中で,CPUはI/O処理の完了を待たずに,実行可能なほかの処理を実行する。その結果,Webサーバは複数のプロセスやスレッドを生成する必要がなく,スマホアプリへも効率的にサービスを提供できる。

〔リアクタパターンの調査〕

ノンブロッキングI/O型のサーバソフトで,Webアプリを動作させるためには,非同期処理の考え方に基づいたソフトウェア設計が必要である。そこで,Y君は,ノンブロッキングI/O型の処理を実現するデザインパターンの一つであるリアクタパターンについて調査した。図1にY君が調査したリアクタパターンの処理の流れを示す。

図1 Y君が調査したリアクタパターンの処理の流れ
図の説明テキスト

「図1 Y君が調査したリアクタパターンの処理の流れ」を示すシーケンス図。
凡例として以下が定義されている:

  • 同期メッセージ(実線に黒塗りの矢印)
  • 非同期メッセージ(実線に白抜きの矢印)
  • 応答メッセージ(破線に白抜きの矢印)

構成要素(ライフライン)は左から順に「メイン処理」「ハンドラ」「リアクタ」「デマルチプレクサ」。

メッセージのやり取りは以下の通り:

  1. 「メイン処理」から「リアクタ」へ、同期メッセージ「(ア) ハンドラの登録」
  2. 「リアクタ」から「ハンドラ」へ、同期メッセージ「(イ) ハンドラを取得」
  3. 「ハンドラ」から「リアクタ」へ、応答メッセージ(ラベルなし)
  4. 「メイン処理」から「リアクタ」へ、同期メッセージ「(ウ) イベントのハンドル依頼」
  5. 「リアクタ」から「デマルチプレクサ」へ、同期メッセージ「(エ) イベント待ち」
  6. 外部(右側)から「デマルチプレクサ」へ、同期メッセージ「(オ) I/O処理の完了」
  7. 「デマルチプレクサ」から「リアクタ」へ、非同期メッセージ「(カ) イベント」
  8. 「リアクタ」から「ハンドラ」へ、非同期メッセージ「(キ) ハンドラを実行」

I/O処理の処理結果を利用する処理をハンドラとして定義する。次に,I/O処理の完了待ちを依頼したいメイン処理が,①I/O処理完了後に実行するハンドラ名を引数に“(ア)ハンドラの登録”を行うと,リアクタは“(イ)ハンドラを取得”する。次に,メイン処理がリアクタに“(ウ)イベントのハンドル依頼”を行うと,リアクタはデマルチプレクサに“(エ)イベント待ち”を指示する。デマルチプレクサは複数のI/O処理の完了を一括して監視し,“(オ)I/O処理の完了”を検知した場合には,対応する“(カ)イベント”をリアクタに発行する。イベントを受け取ったリアクタは“(キ)ハンドラを実行”する。メイン処理は,イベントのハンドル依頼を行った後は,I/O処理の完了を待たずにほかの処理を実行できる。

リアクタパターンを適用する場合は,遅いI/O処理の次に実行される処理をハンドラとして分割するのがよい。しかし,リアクタパターンに基づき設計されたプログラムは,②保守性が下がるおそれがある。

〔QRコード作成処理の設計〕

Y君は,リアクタパターンを用いて,スマホアプリからのアクセスに対する応答時間が最小になるように,QRコード作成処理を設計した。図2にY君が設計したQRコード作成処理の流れを示す。

図2 Y君が設計したQRコード作成処理の流れ
図の説明テキスト

図2 Y君が設計したQRコード作成処理の流れ
シーケンス図。
構成要素(オブジェクト): メイン処理, ハンドラ1, ハンドラ2, ハンドラ3, リアクタ
メッセージのやり取りと処理:

  • メイン処理内で「処理1」「処理c」を実行
  • メイン処理からリアクタへ非同期メッセージ「(ウ) イベントのハンドル依頼」を送信
  • リアクタからハンドラ1へ非同期メッセージ「(キ) ハンドラを実行」を送信
  • ハンドラ1内で「処理d」「処理e」を実行
  • ハンドラ1からリアクタへ非同期メッセージ「(ウ) イベントのハンドル依頼」を送信
  • リアクタからハンドラ2へ非同期メッセージ「(キ) ハンドラを実行」を送信
  • ハンドラ2内で「処理f」を実行
  • ハンドラ2からリアクタへ非同期メッセージ「(ウ) イベントのハンドル依頼」を送信
  • リアクタからハンドラ3へ非同期メッセージ「(キ) ハンドラを実行」を送信
  • ハンドラ3内で「処理7」を実行
    注記:
    注記1 図1中の(ア), (イ)の同期メッセージは省略
    注記2 図1中の(エ)〜(カ)の非同期メッセージは省略
    注記3 図1中の応答メッセージは省略

その後Y君は,新システムのWebアプリの開発を完了させ,X社の会員はスマートフォンを通じてクーポン券を利用することが可能となった。

設問と解答・解説

設問1

〔Webアプリの処理方式の調査〕について,(1),(2)に答えよ。

(1)配点 2点

本文中の a に入れる適切な数値を答えよ。答えは,小数第3位を四捨五入して,小数第2位まで求めよ。

模範解答

99.77

解説

表1「QRコード作成処理」より、全体の処理時間は各処理(1〜7)の処理時間の合計となる。

  • 0.02+10+0.06+3+15+5+2=35.080.02 + 10 + 0.06 + 3 + 15 + 5 + 2 = 35.08(ミリ秒)

次に、表1の「処理区分」が「I/O処理」となっているものの処理時間を合計する。該当するのは処理2、4、5、6、7である。

  • 10+3+15+5+2=3510 + 3 + 15 + 5 + 2 = 35(ミリ秒)

したがって、全体の処理時間に占めるI/O処理の完了待ち時間の割合は以下の式で求められる。

  • 35÷35.08×100=99.7719…35 \div 35.08 \times 100 = 99.7719\dots(%)

問題の指示に従い、小数第3位を四捨五入すると 99.77% となる。

各選択肢の解説

本問は数値解答のため選択肢はないが、計算ミス(I/O処理の抽出漏れなど)に注意が必要である。

(2)配点 2点

本文中の b に入れる適切な字句を答えよ。

模範解答

コンテキスト

採点基準

知識・理解度(内容)1点
  • 1点: 「コンテキスト」という用語を正確に提示している。
  • 0点: 「コンテキスト」という用語が提示されていない、または不正確である。
論理性(構造)1点
  • 1点: 文脈に適した用語を選択できている。
  • 0点: 文脈に適した用語を選択できていない。

解説

本文中の「プロセスやスレッドの切替え処理」を指すIT用語を答える問題である。

CPUが一つのプロセスやスレッドを実行している状態から、別のプロセスやスレッドの実行へと切り替える際、現在のCPUの状態(レジスタ値など)を退避し、次に実行するプロセスの状態を復元する処理のことを コンテキストスイッチ と呼ぶ。

多数のアクセスを処理するためにプロセスやスレッドを増やしすぎると、このコンテキストスイッチに要する時間(オーバーヘッド)が増大し、かえって処理効率が低下する原因となる。

高得点のポイント

  • スレッドスイッチなどの誤答を避け、「コンテキスト」という用語を正確に記述すること。

設問1(2)は,正答率が低かった。コンテキストスイッチについて問うたが,スレッドスイッチという解答が散見された。コンテキストスイッチは,一つのCPUコアを有効に利用するためにプロセスやスレッドを切り替える重要な処理なので,是非理解してほしい。

設問2

〔リアクタパターンの調査〕について,(1),(2)に答えよ。

(1)配点 1点

本文中の下線①について,関数呼出しの引数として渡される関数のことを何というか,解答群の中から選び,記号で答えよ。

ア. callback関数
イ. static関数
ウ. template関数
エ. virtual関数

模範解答

選択肢ア: callback関数

解説

リアクタパターンにおいて、「I/O処理完了後に実行するハンドラ」を登録する仕組みに関する問題である。

特定のイベント(ここではI/O処理の完了)が発生した際に呼び出されることを前提として、他の関数に引数として渡しておく(登録しておく)関数のことを コールバック関数(callback関数) と呼ぶ。メイン処理はコールバック関数を登録しておくことで、自身はI/O処理の完了を待たずに別の作業を進めることができる。

各選択肢の解説

  • ア (callback関数): 正解。イベント発生時にシステム側から呼び出される関数のことである。
  • イ (static関数): 静的関数とも呼ばれ、オブジェクト指向言語においてインスタンスではなくクラス自体に属する関数のことである。
  • ウ (template関数): C++などの言語において、データ型に依存しない汎用的な関数を定義する機能のことである。
  • エ (virtual関数): 仮想関数とも呼ばれ、オブジェクト指向言語においてサブクラスでのオーバーライド(再定義)を前提とした関数のことである。
(2)配点 1点

本文中の下線②について,プログラムの保守性が下がる理由を15字以内で述べよ。

模範解答

可読性が下がるから

採点基準

知識・理解度(内容)1点
  • 1点: 非同期処理の分割によるコードの可読性・理解のしやすさの低下を指摘できている。
  • 0点: 記述がない、または的外れな内容である。

解説

リアクタパターンでは、本来一連の流れとして実行されるべき処理を、I/O処理ごとに「ハンドラ」として細かく分割する必要がある。

分割されたハンドラはイベント駆動で非同期に呼び出されるため、ソースコードが記述されている順番と、実際にプログラムが実行される順番が一致しなくなる。その結果、プログラム全体の処理の流れを追いづらくなり、可読性が下がる(理解しにくくなる)ことで保守性が低下する。

高得点のポイント

  • 処理の流れが把握しづらくなること、または可読性が低下することを15字以内で簡潔に表現できていること。

設問3

〔QRコード作成処理の設計〕について,(1),(2)に答えよ。

(1)配点 3点

図2中の c に入れる適切な処理番号を,表1中の処理番号を用いて答えよ。ただし,複数ある場合は全て答えよ。

模範解答

2

採点基準

知識・理解度(内容)2点
  • 2点: 処理順序とリアクタパターンの役割に基づき、正しく「2」を導出できている。
  • 1点: 処理の順序関係を一部理解しているが、最終的な導出に至っていない。
  • 0点: 記述がない、または的外れな内容である。
論理性(構造)1点
  • 1点: メイン処理で実行すべき最初のI/O処理を論理的に判断できている。
  • 0点: 論理的な判断ができていない。

解説

図2の「QRコード作成処理の流れ」を見ると、メイン処理の中で「処理1」に続いて「処理c」が実行されている。

表1を確認すると、「処理1」の開始条件は「なし」である。次に実行可能なのは、「処理1の完了」を開始条件としている 処理2 である。

本文の「遅いI/O処理の次に実行される処理をハンドラとして分割する」という記述から、メイン処理は最初のI/O処理である「処理2」を開始し、その完了待ちをリアクタに依頼して自身の処理を終える構成になる。

したがって、cに入るのは 2 である。

高得点のポイント

  • 表1の開始条件に基づき、処理1の次に実行される処理が処理2であることを特定できていること。
(2)配点 3点

図2中の d に入れる適切な処理番号を,表1中の処理番号を用いて答えよ。ただし,複数ある場合は全て答えよ。

模範解答

3

3,5

採点基準

知識・理解度(内容)2点
  • 2点: 「3」又は「3,5」と解答している(「5,3」や区切り記号の揺れは可)。
  • 1点: 処理3を含むが過不足がある(例:3,4)。
  • 0点: 処理3を含まない。
論理性(構造)1点
  • 1点: 処理2の完了を開始条件とする処理(3:CPU処理、5:I/O処理)をハンドラ1で扱う依存関係に沿っている。e との組合せの整合は e 側で判定する。
  • 0点: 処理の依存関係に沿っていない。

解説

図2において、ハンドラ1は「処理2」の完了イベントを受けてリアクタから呼び出されている。

表1を確認すると、「処理2の完了」を開始条件としているのは、処理3(CPU処理)と 処理5(I/O処理)の2つである。

ハンドラ1の中では、CPU処理である「処理3」を実行した後、その完了を条件とする「処理4(I/O処理)」の開始を行う必要がある。同時に、「処理5」の開始も行う。

処理4は処理3の完了後でなければ開始できず、処理5は処理2完了後ならハンドラ1内のどこで開始してもよいので、(d,e)=(3, 4と5) 又は (3と5, 4) の2通りが正解で、同じ組の中で答える必要がある。

高得点のポイント

  • ハンドラ1が「処理2の完了」をトリガーとして実行されることを理解できていること。
  • 表1から、処理2の完了を開始条件とする処理(処理3、5)を見つけ出せていること。
(3)配点 3点

図2中の e に入れる適切な処理番号を,表1中の処理番号を用いて答えよ。ただし,複数ある場合は全て答えよ。

模範解答

4,5

4

採点基準

知識・理解度(内容)2点
  • 2点: d の解答と同じ群の e:d が「3」なら e「4,5」(順序・区切りの揺れ可)、d が「3,5」なら e「4」。d が両群のどちらでもない(誤り・無回答)ときは e が「4,5」又は「4」なら2点。
  • 1点: 処理4を含むが、群をまたがずに過不足がある(例:4,6)。
  • 0点: 群をまたいだ組合せ(d=3 で e=4 → 処理5がどこでも実行されない。d=3,5 で e=4,5 → 処理5が重複)、又は処理4を含まない。
論理性(構造)1点
  • 1点: 処理4を処理3の完了後に開始する依存関係に沿っている(群をまたいだ組合せは0)。
  • 0点: 処理の依存関係に沿っていない。

解説

図2において、ハンドラ1は「処理2」の完了イベントを受けてリアクタから呼び出されている。

表1を確認すると、「処理2の完了」を開始条件としているのは、処理3(CPU処理)と 処理5(I/O処理)の2つである。

ハンドラ1の中では、CPU処理である「処理3」を実行した後、その完了を条件とする「処理4(I/O処理)」の開始を行う必要がある。同時に、「処理5」の開始も行う。

処理4は処理3の完了後でなければ開始できず、処理5は処理2完了後ならハンドラ1内のどこで開始してもよいので、(d,e)=(3, 4と5) 又は (3と5, 4) の2通りが正解で、e は d と同じ組で答える必要がある。

高得点のポイント

  • ハンドラ1の中で、処理3(CPU処理)の実行だけでなく、処理4・処理5(I/O処理)の開始が行われていることを読み取れていること。
(4)配点 3点

図2中の f に入れる適切な処理番号を,表1中の処理番号を用いて答えよ。ただし,複数ある場合は全て答えよ。

模範解答

6

採点基準

知識・理解度(内容)2点
  • 2点: 後続処理の開始条件から逆算し、正しく「6」を導出できている。
  • 1点: 処理の依存関係を一部理解しているが、最終的な導出に至っていない。
  • 0点: 記述がない、または的外れな内容である。
論理性(構造)1点
  • 1点: ハンドラの実行順序を逆算して論理的に組み立てられている。
  • 0点: 論理的に組み立てられていない。

解説

図2において、ハンドラ2からリアクタへのイベントハンドル依頼を経て、ハンドラ3が実行されている。

ハンドラ3で実行されているのは「処理7」である。表1より、「処理7」の開始条件は「処理6の完了」である。
つまり、ハンドラ3は「処理6の完了イベント」を受けて実行されており、その前段のハンドラ2の中で 処理6 が開始され、イベントハンドルが依頼されていなければならない。

確認のため表1を見ると、「処理6」の開始条件は「処理4の完了」である。したがって、ハンドラ2は「処理4の完了イベント」を受けて実行されるハンドラとして整合性が取れる。

ゆえに、fには 6 が入る。

高得点のポイント

  • ハンドラ3で実行される処理7の開始条件から逆算し、ハンドラ2が処理6を担当することを論理的に導き出せていること。
(5)配点 2点

図2のようにQRコード作成処理を設計した場合,処理4~7はどのような順序で完了するか,処理が早く完了する順にコンマ区切りで答えよ。

模範解答

4,6,7,5

採点基準

知識・理解度(内容)1点
  • 1点: 各処理の処理時間を正しく把握し、並行処理を考慮した完了時間を計算できている。
  • 0点: 完了時間の計算が不正確である、または並行処理を考慮できていない。
論理性(構造)1点
  • 1点: 導き出した完了時間に基づいて、正しい順序で処理を並べられている。
  • 0点: 正しい順序で並べられていない。

解説

表1の処理時間を用いて、非同期に実行される処理4~7の完了時間を計算する。
基準として、「処理2」が完了した時点を 0ミリ秒 とする。

処理2完了後、ハンドラ1内で「処理3(0.06ミリ秒)」が実行され、その後直ちに「処理4」と「処理5」が並行して開始される。CPU処理である処理3やハンドラの処理時間は非常に短いため、ここでは処理4と処理5はほぼ0ミリ秒時点で同時に開始されると考えて計算する。

  • 処理4:処理時間は3ミリ秒なので、開始から 3ミリ秒後 に完了する。
  • 処理6:処理4完了後に開始され、処理時間は5ミリ秒。完了時間は 3+5=3 + 5 = 8ミリ秒後 となる。
  • 処理7:処理6完了後に開始され、処理時間は2ミリ秒。完了時間は 8+2=8 + 2 = 10ミリ秒後 となる。
  • 処理5:処理4と同時に開始され、処理時間は15ミリ秒。完了時間は開始から 15ミリ秒後 となる。

したがって、完了時間が早い順に並べると、3ミリ秒(処理4)→ 8ミリ秒(処理6)→ 10ミリ秒(処理7)→ 15ミリ秒(処理5)となる。
よって答えは「4,6,7,5」である。

高得点のポイント

  • リアクタパターンにおいて、処理4と処理5が並行して実行開始される(非同期処理)ことを理解していること。
  • 各処理の処理時間を累積して、正確な完了時間を算出できていること。

設問3は,正答率が平均的であった。本文に示したデザインパターンに従ってソフトウェアを設計し,その処理順序について問う問題であった。デザインパターンを理解してソフトウェアを設計する能力は,良いソフトウェアを設計する上で重要なので,是非身に付けてほしい。

Spineチーム