#はじめに
2020年の1月末までにMicrosoft認定試験の一つの70-483 Programming in C#を受験し、合格しなければなりません!(受かってねと上の方から言われた…)
そのため、受験対策書籍として挙げられている『Exam Ref 70-483 Programming in C# (MCSD)』(英書)を受験対策として読んでいます。因みに、日本語の参考書籍は挙げられていません。(当然だよね…)
この記事では、この書籍で学べるスキルと要約を章ごとに日本語訳として纏めます。(まずは第1章の内容のみ、そしてルー語多め)
尚、70-483 Programming in C#試験をパスするためのHow to記事ではありませんので、予めご了承ください。
#Ch.1 プログラムフローの管理
##学べるスキル
- マルチスレッド及び非同期処理の実装
- マルチスレッドの管理
- プログラムフローの実装
- イベントとコールバックの作成と実装
- 例外処理の実装
##要約
- マルチスレッドは多数の協力プロセスで構成されるアプリケーションを実現する。
- マルチスレッドによるアプリケーションの作成において、その挙動は多数の協力タスクに展開される。ホストコンピューターが複数のCPUを持つ場合、それらのタスクは並列処理によって実行される。単体CPUの場合は、総当たり(round-robin)方式でアクティブタスクを順番に実行していく。
- マルチスレッドアプリケーションの作成は、既存の動作のひとまとまりを取得し、
Task.Parallel
ライブラリあるいはParallel LINQ
のタスクとして実行することで容易に行える。 - .NETの
Task
クラスは実行タスクの高水準の抽象クラス(抽象化)を提供する。タスクは値を返し、1つ以上のタスクが完了すると、継続タスクが自動的に実行される。子タスクを開始し、すべての子タスクが完了するまで親タスクを待たせることもできる。 - .NETの
Thread
クラスは実行タスクの低水準の抽象クラスを提供する。スレッドは結果を返すことも、継続スレッドを作成することもできない。だが、スレッドはフォアグラウンドまたはバックグラウンドプロセスとして実行できる。タスクはバックグラウンドプロセスとしてのみ実行できる。フォアグラウンドプロセスは完了するまで実行されるが、バックグラウンドプロセスはそれを作成したフォアグラウンドプロセスが完了すると終了する。スレッドの優先度を設定することもできる(これは純粋に助言的なものだが、特定のスレッドがプロセッサへの特定レベルのアクセスを許可されることを保証しない)。 -
async
とawait
を利用することで容易にマルチスレッドアプリケーションを作成できる。await
キーワードは、async
キーワードによって非同期であると識別されたメソッドの呼び出しに先行する。非同期メソッドは、Task
(そのメソッドがvoid
の場合)またはTask<type>
(そのメソッドである値が返される場合)を返すことができる。コンパイラはawait
キーワードに達したときに呼び出し元に戻る非同期メソッドに従うコードを生成する。await
に続く記述は同時に実行される。ボタンを押すことに結びつけられた非同期動作は、ボタンによって実行される動作が完了するまでUIを停止するのではなく、最初のawait
キーワード部でUIのロックを解除する。アプリケーションは、待機中の動作を例外ハンドラーで囲むことにより、非同期呼び出し中にスローされた例外を捕らえられるが、これは待機中の動作が値を返す場合にのみ機能する。この理由から、void
非同期呼び出しは、成功したかどうかを判断する方法がないため、避けるべき。 - 標準的な.NETにおけるcollectionクラスはスレッドセーフではない。これは、複数のタスクがリストを使用してデータを共有しようとすると、リストの内容が破損することを意味する。.NETフレームワークは複数のアクティブタスクで共有できる同時コレクションの集合を提供する。
BlockingCollection
クラスは同時コレクションにおけるConcurrentStack
,ConcurrentQueue
およびConcurrentBag
を提供する。BlockingCollection
を使用したタスクは、追加アイテムのための余地がない場合あるいは空集合からアイテムの取得を試みる場合にブロック(停止)される。同時コレクションは動作が成功したかどうかを返すアイテムを抽出するメソッドの"try"バージョンを提供する。これは使用可能なアイテムがあると判断してから読み取るまでの間に、別のタスクがアイテムを削除した可能性があるためである。ConcurrentDictionary
クラスは、辞書内のアイテムの暫定的なアップデートのための追加メソッドを提供する。 - マルチスレッドアプリケーションは、共有データアイテムに対するタスクによる動作が、そのタスクが別のタスクに置き換えられる前に完了状態に達しない競合状態に対して脆弱である。
- 競合状態は、アクションをアトミックにすることで対処できる。これにより、アクションは、別のタスクがアクションを実行する前に常に完了する。タスクはロックを要求し、ロックを保持している間、ロックを要求してアトミックコードを入力しようとする他のタスクはブロックされる。これにより、ブロックされたタスクのキューが特定のロックの解放を待機する場合がある。
- 互いにロックを待機している2つのタスクは、
deadlocked
またはin a deadly embrace
と言われる。デッドロックは、アトミックアクション内でロックオブジェクトへのアクセスを待機するコードの結果として発生する可能性がある。 これは適切な設計での対処が必要である。 - アプリケーションがロックを取得しようとするとすぐにブロックされるのではなく、タスクがアトミックアクションにアクセスできるかどうかを判断できるという利点がある場合、ロックの監視メカニズムは、
lock
キーワードよりも優先して使用できる。 - 特定の変数の更新などの単純なアクションは、コードのアトミックブロックを作成するのではなく、インターロックされた操作を使用してアーカイブできる。
- 複数のプロセスで使用される可能性のある変数は、「変わりやすいもの(volatile)」としてマークできる。これにより、コンパイラは、変数の値をプロセッサー・レジスターにキャッシュしたり、命令の順序を変更するなどの最適化を実行しないようになる。
- タスクは、コレクショントークンを使用してキャンセルできる。タスクが実行されると、トークンの状態を確認して、キャンセルが要求されたかどうかを判断する必要がある。 これが、
Task
とThread
の重要な違いである。スレッドはいつでも別のプロセスによって中止できる。 1つのタスクは別のタスクのキャンセルを要求でき、実際には、タスク内のコードがキャンセルトークンをチェックしている場合にのみ、そのスレッドが終了する。ただし、タスクはバックグラウンドで実行され、タスクを作成したフォアグラウンドプロセスが完了すると自動的に終了するため、アクティブなタスクをメモリから削除できないという意味ではない。 - オブジェクト内のメソッドは、複数のタスクを含むアプリケーションで使用する場合、スレッドセーフにする必要がある。スレッドセーフメソッドを含むクラスのデータメンバーへのアクセスは、アトミックな方法で管理する必要がある。参照によってメソッドに渡されるパラメーターは、メソッドの実行中に発生する可能性のあるパラメーターの要素の変更に対して脆弱である。
-
while
構文は、条件が真である限り何かを繰り返したい場合に有用で。do-while
構文は、何かを実行し、アクションが失敗した場合にそれを繰り返す場合に役立つ。 -
for
構文は、ループで初期化、テスト、および更新アクションを実行する簡単な方法である。 for構文では、カウンター値の管理が頻繁に行われるが、これがこの構文を使用できる唯一の方法ではない。 -
foreach
構文を使用して、コレクション内のアイテムを列挙できる。 このコレクションは、列挙子を提供するメソッドを提供する。列挙子は、foreach
構造によって反復され、コレクション内のアイテムは読み取り専用として提供される。 -
break
ステートメントを使用すると、プログラムはループをすぐに終了できる。 ループ内の多数のbreak
は、ループが終了した状況を識別するのを難しくする可能性がある。 -
continue
ステートメントを使用すると、プログラムはループの「トップ」に戻り、ループコードをそれ以上進むことなくループを繰り返すことができる。continue
ステートメントの実行時に、更新およびテストの動作が実行されることに注意。 -
if
構文は、ステートメントまたはステートメントのブロックの条件付き実行を許可する。if
構文が論理式によって制御され、条件が偽の場合に実行されるステートメントを識別するelse
句が後に続く場合がある。 条件をネストできる場合、if
構文のelse
部分は常に「最も近い」if
構文と結合する。 - 論理式は、
true
またはfalse
に評価され、変数は関係演算子および等価演算子を使用して比較できる。AND(論理積)&
,OR(論理和)|
,XOR(排他的論理和)^
演算子を使用して論理値を組み合わせることができる。AND演算子とOR演算子には、式の結果の値を決定できるポイントまでしか評価されない「短絡」バージョン(条件AND&&
, 条件OR||
)がある。 -
switch
構文により、整数、文字列、または文字である制御値の値から特定の動作を選択できる。 コントロール値がどの選択とも一致しない場合、デフォルトの動作を指定できる。 - 式には、オペランドと演算子が含まれる。 オペランドはリテラル値または変数のことである(式中の演算子以外の要素)。演算子には、式の評価中に演算子が適用されるポイントを決定する優先度と関連性がある。
- C#プログラムは、デリゲート
delegate
を使用して、オブジェクトのメソッドへの参照として機能する変数を作成できる。パブリッシャーから通知を受信するオブジェクトは、デリゲートを使用して、イベントパブリッシャーによって呼び出されるメソッドを指定できる。単一のパブリッシャーデリゲートは、各式のサブスクライバーを呼び出す。 -
event
キーワードを使用すると、デリゲートを安全な方法で使用でき、EventArgs
クラスは、.NET全体で使用されるパターンを記述して、イベントがサブスクライバーへのデータの配信を可能とする。 - デリゲートは、個々のメソッドへの参照としても使用できる。 メソッドを参照するデリゲートは、アクションを記述するデータの一部と見なすことができる。
- ラムダ式を使用すると、アクションを直接表現でき、コードを記述するときに便利な速記ができる。ラムダ式によって返されるパラメーターのタイプと値は、呼び出しのコンテキスト(内容)から推測される。
- 例外(exception)は、プログラムが正常な動作を継続することができない状況を示すために、プログラムによってスローされる。実行時、コードの
try
ブロックで実行されているステートメントから、catch
ブロックの例外ハンドラーに転送されます。これは、例外を処理するために記述する。try
構文内にないコードでスローされた例外は、実行中のスレッドを終了させる。 -
finally
要素をtry
構文で使用して、常に実行されるコードを指定できる。 - プログラムは、新しい例外インスタンスを作成し、
throw
キーワードを使用することにより、例外を投げる(スローする)。すべての例外オブジェクトは、Exception
クラスから派生している。エラー条件を記述するために.NETライブラリで使用される多数の例外タイプが存在する。 プログラマーは、エラー固有の情報を含む独自の例外タイプを作成することもできる。 - 例外オブジェクトには、例外がスローされるプログラムソース内のポイントを示す「スタックトレース」など、エラーを説明する情報が含まれる。例外には、内部例外(inner exception)参照を含めることもできるため、例外管理の別のレイヤーへの新しい例外が発生し得る。
続編
随時、更新予定。