応用情報技術者 2021年 春期 午後 問08
クーポン券発行システムの設計に関する次の記述を読んで、設問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処理の完了待ち時間となる。

このため、ブロッキング I/O 型のサーバソフトでは、複数のスマホアプリにサービスを提供するために、プロセスやスレッドを複数生成している。しかし、プロセスやスレッドの数が増えると、プロセスやスレッドの切替え処理である b スイッチがボトルネックとなり、CPU やメモリを追加してもスマホアプリからの同時アクセスへの対応は困難となる。
そこで Y 君は、多数のスマホアプリからのアクセスを効率よく処理できるノンブロッキング I/O 型のサーバソフトの利用を検討した。ノンブロッキング I/O 型のサーバソフトでは、一つのプロセスやスレッドの中で、CPU は I/O 処理の完了を待たずに、実行可能なほかの処理を実行する。その結果、Webサーバは複数のプロセスやスレッドを生成する必要がなく、スマホアプリへも効率的にサービスを提供できる。
〔リアクタパターンの調査〕
ノンブロッキング I/O 型のサーバソフトで、Webアプリを動作させるためには、非同期処理の考え方に基づいたソフトウェア設計が必要である。そこで、Y 君は、ノンブロッキング I/O 型の処理を実現するデザインパターンの一つであるリアクタパターンについて調査した。図1に Y 君が調査したリアクタパターンの処理の流れを示す。

I/O 処理の処理結果を利用する処理をハンドラとして定義する。次に、I/O 処理の完了待ちを依頼したメイン処理が、① I/O 処理完了後に実行するハンドラ名を引数に “(ア)ハンドラの登録”を行うと、リアクタは “(イ) ハンドラを取得”する。次に、メイン処理がリアクタに“(ウ) イベントのハンドル依頼”を行うと、リアクタはデマルプレクサに“(エ) イベント待ち”を指示する。デマルプレクサは複数の I/O 処理の完了を一括して監視し、“(オ) I/O 処理の完了”を検知した場合には、対応する“(カ) イベント”をリアクタに発行する。イベントを受け取ったリアクタは“(キ) ハンドラを実行”する。メイン処理は、イベントのハンドル依頼を行った後は、I/O 処理の完了を待たずにほかの処理を実行できる。
リアクタパターンを適用する場合は、遅い I/O 処理の次に実行される処理をハンドラとして分割するのがよい。しかし、リアクタパターンに基づき設計されたプログラムは、②保守性が下がるおそれがある。
〔QR コード作成処理の設計〕
Y君は、リアクタパターンを用いて、スマホアプリからのアクセスに対する応答時間が最小になるように、QR コード作成処理を設計した。図2に Y君が設計した QR コード作成処理の流れを示す。

その後 Y 君は、新システムの Webアプリの開発を完了させて、X社の会員はスマートフォンを通じてクーポン券を利用することが可能となった。
設問1:〔Webアプリの処理方式の調査〕について、(1)、(2)に答えよ。
(1)本文中のaに入れる適切な数値を答えよ。答えは、小数第3位を四捨五入して、小数第2位まで求めよ。
模範解答
a:99.77
解説
解答の論理構成
- I/O 待ち時間に該当する処理を特定
【問題文】の「表1 QRコード作成処理」より、I/O 処理とされている時間は
“10”, “3”, “15”, “5”, “2” 〔単位はいずれもミリ秒〕です。 - CPU が実際に演算している時間を確認
同じく表1の CPU 処理は “0.02” と “0.06” です。 - 全体の処理時間を計算
I/O 合計
CPU 合計
全体 - I/O 待ち時間の割合を算出
- 指定どおり「小数第3位を四捨五入して、小数第2位まで」表す
したがって a には “99.77” が入ります。
誤りやすいポイント
- CPU 処理 “0.08” ms を見落とし、全体を “35” ms としてしまう。
- 小数第3位(第3桁)で四捨五入する指示を忘れ、99.772… をそのまま書く。
- 「I/O 処理=待ち時間」という前提を読み飛ばし、CPU 時間も含めてしまう。
FAQ
Q: CPU 処理中にも若干の待ちが発生すると考えるべきですか?
A: 【問題文】ではブロッキング I/O 型サーバを前提とし、「I/O 処理を行う場合、CPU は低速な I/O 処理の完了を待つ」と明示されているため、待ち時間は I/O 区分の時間のみと判断します。
A: 【問題文】ではブロッキング I/O 型サーバを前提とし、「I/O 処理を行う場合、CPU は低速な I/O 処理の完了を待つ」と明示されているため、待ち時間は I/O 区分の時間のみと判断します。
Q: 数字をパーセントに直す際、小数点以下をどこまで書けば良いですか?
A: 指示は「小数第3位を四捨五入して、小数第2位まで」なので “99.77” と 2 桁でまとめます。
A: 指示は「小数第3位を四捨五入して、小数第2位まで」なので “99.77” と 2 桁でまとめます。
Q: I/O 待ち比率が高いほどノンブロッキング I/O の効果は大きいのですか?
A: はい。CPU がほとんど待機している状態(今回の “99.77 %”)では、ノンブロッキング I/O で待機時間を他処理に転用できるため、スループット向上が期待できます。
A: はい。CPU がほとんど待機している状態(今回の “99.77 %”)では、ノンブロッキング I/O で待機時間を他処理に転用できるため、スループット向上が期待できます。
関連キーワード: ブロッキングI/O, ノンブロッキングI/O, CPU使用率, リアクタパターン, 処理時間割合
設問1:〔Webアプリの処理方式の調査〕について、(1)、(2)に答えよ。
(2)本文中のbに入れる適切な字句を答えよ。
模範解答
b:コンテキスト
解説
解答の論理構成
- 【問題文】には
“しかし、プロセスやスレッドの数が増えると、プロセスやスレッドの切替え処理である b スイッチがボトルネックとなり…”
と記載されています。 - プロセスやスレッドを切り替える際に OS が実行するのは、レジスタ・スタックポインタ・メモリマップなどの実行状態(=コンテキスト)を保存/復元する処理です。
- これを一般に “コンテキストスイッチ” と呼びます。
- したがって b に入る語は “コンテキスト” となります。
誤りやすいポイント
- “スレッドスイッチ” や “プロセススイッチ” と書いてしまう
→ 切替えの対象ではなく、切替え時に扱う実行状態の名称を問う設問です。 - “タスクスイッチ” と答える
→ タスク=スレッドと解釈しても誤りではない場面がありますが、用語統一を求める試験では “コンテキストスイッチ” が正式。 - “コンテクスト” と表記ゆれを起こす
→ 【問題文】に合わせ “コンテキスト” とするのが安全です。
FAQ
Q: コンテキストスイッチはなぜボトルネックになるのですか?
A: レジスタ退避・復元、キャッシュ無効化、メモリマッピング変更など CPU にとって重い処理が発生し、頻度が増えると実行時間の多くを占めてしまうためです。
A: レジスタ退避・復元、キャッシュ無効化、メモリマッピング変更など CPU にとって重い処理が発生し、頻度が増えると実行時間の多くを占めてしまうためです。
Q: ノンブロッキング I/O 型サーバではコンテキストスイッチがなくなるのですか?
A: “一つのプロセスやスレッドの中で” 処理を回す設計により切替え頻度を大幅に減らせますが、ゼロになるわけではありません。OS 由来の割込みなどで最小限は発生します。
A: “一つのプロセスやスレッドの中で” 処理を回す設計により切替え頻度を大幅に減らせますが、ゼロになるわけではありません。OS 由来の割込みなどで最小限は発生します。
Q: コルーチンや async/await もコンテキストスイッチを伴いますか?
A: ユーザ空間で行う “協調的切替え” なので、OS が行うコンテキストスイッチより軽量ですが、スタック保存などの処理はあるため完全に無料ではありません。
A: ユーザ空間で行う “協調的切替え” なので、OS が行うコンテキストスイッチより軽量ですが、スタック保存などの処理はあるため完全に無料ではありません。
関連キーワード: ブロッキングI/O, ノンブロッキングI/O, コンテキストスイッチ, マルチスレッド, リアクタパターン
設問2:〔リアクタパターンの調査〕について、(1)、(2)に答えよ。
(1)本文中の下線①について、関数呼出しの引数として渡される関数のことを何というか、解答群の中から選び、記号で答えよ。
解答群
ア:callback関数
イ:static関数
ウ:template関数
エ:virtual関数
模範解答
ア
解説
解答の論理構成
- 問題文では、リアクタパターンの手順を説明する流れの中で、
「① I/O 処理完了後に実行するハンドラ名を引数」と記述されています。 - ここで強調されているのは「ハンドラ名を引数」として渡すという点です。すなわち「関数を関数の引数として受け渡す」仕組みです。
- ソフトウェア設計・プログラミングの一般用語で、呼び出し元が処理を預け、完了後に呼び戻してもらう関数を「callback(コールバック)関数」と呼びます。
- 解答群の中で、関数を引数として渡す概念を表すのは
「ア:callback関数」しかありません。 - 以上より、解答は ア となります。
誤りやすいポイント
- 「static関数」「virtual関数」は C++ の限定的な修飾子に関する語であり、引数として渡すかどうかは無関係なので注意が必要です。
- 「template関数」はジェネリックに型を受け取る仕組みで、イベントハンドリングや非同期処理とは直接関係がありません。
- callback 関数は「イベント駆動=必ず非同期」という誤解に注意。同期的に呼び出すコールバックも存在します。
FAQ
Q: callback 関数とラムダ式は同じですか?
A: ラムダ式は関数を簡潔に記述する文法、callback は「呼び戻し」という利用形態を指します。ラムダ式を callback 関数として渡すことはできますが、概念としては別です。
A: ラムダ式は関数を簡潔に記述する文法、callback は「呼び戻し」という利用形態を指します。ラムダ式を callback 関数として渡すことはできますが、概念としては別です。
Q: リアクタパターンでは callback 関数が必須ですか?
A: パターンの本質は「I/O 完了イベントに対し登録済みハンドラを呼び出す」ことなので、実装形態は関数ポインタ・オブジェクト・インタフェース実装など何でも構いません。関数を直接渡す場合は callback 関数になります。
A: パターンの本質は「I/O 完了イベントに対し登録済みハンドラを呼び出す」ことなので、実装形態は関数ポインタ・オブジェクト・インタフェース実装など何でも構いません。関数を直接渡す場合は callback 関数になります。
Q: callback 関数が多用されると保守性が下がると言われる理由は?
A: 処理の流れが「登録→イベント→実行」と分断され、関数がネストや連鎖で増えるためです。【問題文】でも「②保守性が下がるおそれ」と警告しています。
A: 処理の流れが「登録→イベント→実行」と分断され、関数がネストや連鎖で増えるためです。【問題文】でも「②保守性が下がるおそれ」と警告しています。
関連キーワード: コールバック, 非同期処理, イベント駆動
設問2:〔リアクタパターンの調査〕について、(1)、(2)に答えよ。
(2)本文中の下線②について、プログラムの保守性が下がる理由を15字以内で述べよ。
模範解答
可読性が下がるから
解説
解答の論理構成
-
問題文には「“リアクタパターンを適用する場合は、遅い I/O 処理の次に実行される処理をハンドラとして分割するのがよい。”」とあります。
‐ ここで処理が“ハンドラとして分割”されることが強調されています。 -
さらに「“しかし、リアクタパターンに基づき設計されたプログラムは、②保守性が下がるおそれがある。”」と明記されています。
‐ 保守性低下の直接原因を説明する語句は記載されていませんが、分割により処理が散在することが示唆されています。 -
処理を多数のハンドラに細分化すると、プログラム全体の実行フローが断片化し、追跡しにくくなります。
‐ どのハンドラがどこで呼ばれ、いつ完了するかがコード上で直線的に読めないためです。 -
このように「読みづらさ」が発生すると、コード理解・変更作業が難しくなり、結果として“保守性”が低下します。
-
したがって下線②の理由は「可読性が下がるから」となるわけです。
誤りやすいポイント
- 「パフォーマンスが落ちるから」と答えてしまう
‐ ノンブロッキング I/O はむしろ性能向上を目的としており、論点がずれます。 - 「ハンドラが増えて複雑だから」とだけ書く
‐ “複雑”という言葉より、なぜ保守性に影響するのか(読みにくさ)を示す必要があります。 - “テストが難しいから”と答える
‐ 一理ありますが、問題文にはテスト難易度の話はなく、主因として「可読性」を押さえることが重要です。
FAQ
Q: 保守性と可読性は同じ意味ですか?
A: 保守性は「変更や修正のしやすさ」、可読性は「読みやすさ」を指します。可読性の低下は保守性を直接悪化させる代表例の一つです。
A: 保守性は「変更や修正のしやすさ」、可読性は「読みやすさ」を指します。可読性の低下は保守性を直接悪化させる代表例の一つです。
Q: ハンドラを増やしても設計ドキュメントを整備すれば保守性は下がらないのでは?
A: ドキュメントは助けになりますが、実ソースコードの可読性が低い状態では変更時のバグ混入リスクが高く、保守コストは依然大きくなります。
A: ドキュメントは助けになりますが、実ソースコードの可読性が低い状態では変更時のバグ混入リスクが高く、保守コストは依然大きくなります。
Q: コールバック地獄(callback hell)と同じ問題ですか?
A: はい、リアクタパターンでハンドラが深くネストすると“callback hell”が発生し、読みにくさ・保守しにくさが顕著になります。
A: はい、リアクタパターンでハンドラが深くネストすると“callback hell”が発生し、読みにくさ・保守しにくさが顕著になります。
関連キーワード: リアクタパターン, ノンブロッキングI/O, ハンドラ, 可読性, 保守性
設問3:〔QRコード作成処理の設計〕について、(1)、(2)に答えよ。
(1)図2中のc~fに入れる適切な処理番号を、表1中の処理番号を用いて答えよ。ただし、複数ある場合は全て答えよ(d、e、fは同じ群中の組み合わせとする)。
模範解答
c:2
a群:
d:3
e:4, 5
f:6
b群:
d:3, 5
e:4
f:6
解説
解答の論理構成
-
図2のメイン処理は、ハンドラを呼び出す前に“QRコード作成処理”の入口となるI/Oを実行します。表1では
「処理番号 2 スマホアプリからクーポン券番号を取得」
が最初のI/Oであり、これが完了した時点でリアクタに完了イベントを依頼します。よって
[c]=「2」。 -
処理 2 が終わるとハンドラ1が起動します。ハンドラ1では
- CPUだけで完了する「処理番号 3 クーポン券番号から QRコードの画像データをメモリに作成」
- その直後に始めたいI/O「処理番号 4 QRコードの画像データを画像ファイルに書き出し」
- 会員への応答には関係ないが同時並行させたいI/O「処理番号 5 クーポン券番号を発行履歴としてデータベースに書き込み」
を開始します。
“4”と“5”はどちらを先に依頼しても良く、リアクタに非同期で渡すだけなので 2通りの書き方 が成立します。
• グループa:3 → 4 → 5 を順に書く
• グループb:3 → 5 → 4 を順に書く
したがって - グループa:[d]=「3」、[e]=「4, 5」
- グループb:[d]=「3, 5」、[e]=「4」
-
画像ファイルが出来上がった(処理 4 完了)イベントで起動するハンドラ2は、会員へファイルを返す
「処理番号 6 スマホアプリに QRコードの画像ファイルを返信」
を実行し、完了イベントをリアクタに登録します。よって [f]=「6」。 -
画像の返送が済んだ後にハンドラ3が起動し、「処理番号 7 QRコードの画像ファイルを削除」を実行して一連の処理が終了します(ここは設問対象外)。
以上より
・[c]=2
・(a群) [d]=3, [e]=4,5, [f]=6
・(b群) [d]=3,5, [e]=4, [f]=6
・[c]=2
・(a群) [d]=3, [e]=4,5, [f]=6
・(b群) [d]=3,5, [e]=4, [f]=6
誤りやすいポイント
- 「処理番号 5」は応答に不要だからハンドラ2以降に回す、と決めつけると並列実行できず全体時間が延びる。
- ハンドラの“呼び出し順”と“実際のI/O完了順”を混同し、4と6を同一ハンドラに入れてしまうミス。
- 依存関係を見落とし、4を3より前に置く、または6を4より前に置くなどの順序違反。
FAQ
Q: なぜ「処理番号 2」だけがメイン処理に残るのですか?
A: リアクタパターンでは「遅いI/O+その完了後に動かす処理」をハンドラに切り出します。2は最初のI/Oで、それ以前にCPU処理が無いためメイン処理が直接実行してイベントを登録するのが自然です。
A: リアクタパターンでは「遅いI/O+その完了後に動かす処理」をハンドラに切り出します。2は最初のI/Oで、それ以前にCPU処理が無いためメイン処理が直接実行してイベントを登録するのが自然です。
Q: 「処理番号 5」をハンドラ2に入れてはいけませんか?
A: 可能ですが、6の完了を遅らせたくないため 5 は4と同時に走らせる方が応答時間を短縮できます。その設計を問題は想定しています。
A: 可能ですが、6の完了を遅らせたくないため 5 は4と同時に走らせる方が応答時間を短縮できます。その設計を問題は想定しています。
Q: グループa・bどちらを選んでも減点されませんか?
A: I/O依頼をリアクタに渡す順序は動作に影響しないので、両方とも正解として公表されています。
A: I/O依頼をリアクタに渡す順序は動作に影響しないので、両方とも正解として公表されています。
関連キーワード: ノンブロッキングI/O, リアクタパターン, 非同期処理, イベント駆動, QRコード
設問3:〔QRコード作成処理の設計〕について、(1)、(2)に答えよ。
(2)図2のようにQRコード作成処理を設計した場合、処理4〜7はどのような順序で完了するか、処理が早く完了する順にコンマ区切りで答えよ。
模範解答
4, 6, 7, 5
解説
解答の論理構成
-
依存関係の確認
【問題文】の表1より、各処理の開始条件は以下のとおりです。- 「処理4 QRコードの画像データを画像ファイルに書き出し」の開始条件は「処理3の完了」。
- 「処理5 クーポン券番号を発行履歴としてデータベースに書き込み」の開始条件は「処理2の完了」。
- 「処理6 スマホアプリに QRコードの画像ファイルを返信」の開始条件は「処理4の完了」。
- 「処理7 QRコードの画像ファイルを削除」の開始条件は「処理6の完了」。
-
実行時間の確認
同じく表1から各処理時間(ミリ秒)を引用します。- 「処理4」:3
- 「処理5」:15
- 「処理6」:5
- 「処理7」:2
-
リアクタパターンによる並行実行
【問題文】では「ノンブロッキング I/O 型のサーバソフト」かつ「リアクタパターン」を採用しています。これにより、
「CPU は I/O 処理の完了を待たずに、実行可能なほかの処理を実行する」
ため、依存のない I/O は並行に開始できます。
具体的には「処理5」は「処理2」が終わりさえすればよいので、「処理4→6→7」のチェーンとは独立して並行実行できます。 -
タイムラインを簡易に図示t = 10ms ・・・ 処理2完了 ├─ 処理3 (0.06ms) ─┐ │ │ │ └─ 処理4 (3ms) ─┐ │ │ │ └─ 処理6 (5ms) ─┐ │ │ │ └─ 処理7 (2ms) ─┐ → 20.06ms で完了 └─ 処理5 (15ms) ───────────┘ → 25ms で完了以上より完了順は
「処理4」→「処理6」→「処理7」→「処理5」。 -
よって解答は
4, 6, 7, 5
誤りやすいポイント
- 「処理5」は独立している点を見落とし、4→6→7→5の並行スタートを想定せずに5を先に並べてしまう。
- ノンブロッキング=“必ず同時開始”と短絡し、処理4の開始条件(処理3完了)など基本的な依存関係を無視してしまう。
- 実行時間を比較せず、依存関係だけで順位を決めるミス。処理6(5ms)より処理5(15ms)が長い点を見逃すと順序を誤る。
FAQ
Q: ノンブロッキング I/O ではすべての処理が同時に始まるのですか?
A: いいえ。開始条件が満たされた時点で“CPU が他の待ちをせずに即座に着手できる”だけで、そもそも依存関係がある処理は待ちが発生します。
A: いいえ。開始条件が満たされた時点で“CPU が他の待ちをせずに即座に着手できる”だけで、そもそも依存関係がある処理は待ちが発生します。
Q: 「処理5」をもっと早く終わらせる方法はありますか?
A: データベース更新をバッチ化またはキューイングして非同期書込みにする、あるいは発行履歴をまとめて登録する等の手段が考えられます。
A: データベース更新をバッチ化またはキューイングして非同期書込みにする、あるいは発行履歴をまとめて登録する等の手段が考えられます。
Q: リアクタパターンで「保守性が下がるおそれ」とは何が原因ですか?
A: ハンドラにロジックが分散しコールバックが増えることで、制御フローが追いにくくなる“コールバック地獄”が起こりやすいためです。
A: ハンドラにロジックが分散しコールバックが増えることで、制御フローが追いにくくなる“コールバック地獄”が起こりやすいためです。
関連キーワード: ノンブロッキングI/O, リアクタパターン, 非同期処理, I/O待ち, 依存グラフ


