質問: いつもお世話になっております。 WPFアプリの作成で、SerialPortを使っております。 相手機器からの返信を待つ際、 別スレッドで ``` while(SerialPort.readLine() != "ok"); //okがきた後の処理 ``` という風に、okがくるまでwhileで待ち続けるか、 Recievedイベントでokかどうかを判断するのはどちらがよいでしょうか。 マイコンの場合は、ほかにすべき処理がなければループで延々と待ち続けることはよくあるように思いますが、Windowsアプリでは避けたほうがいいのでしょうか 何もしないwhileループは、CPUをどのくらい食うのか、とかがわかりません また、シリアルポートに限らず、イベントというのは絶対的に信頼していいものなのでしょうか 何かの拍子に、okがきたにもかかわらず取りこぼしてしまうと、永遠に処理がとまりますし・・・(タイムアウトを設定するとかは別問題?) 人間がボタンを押したり、という場合であれば、 「押したのに処理がされない」場合はもう一度ボタンを押す、なんてことができますが・・・。 よろしくお願いいたします。
回答: こんにちは。 > > どちらがよいでしょうか。 > > > ケースバイケースです。 基本的にイベント待ちする方法はポーリング、もしくは、イベント通知を待つのどちらかです。 前者はデッドロックを避けやすいですが、ポーリング間隔を短くするとCPUを無駄に消費しますし、長くすると応答性能が劣化します。 後者はCPU消費を最小限で応答性能を最大化できますが、デッドロックしないよう注意深い設計が必要です。(設計の難易度は高いです。) ちなみにReadLine自体がイベント通知によって実装されていますので、while文に入れたとしてもそれはポーリングではありません。イベント通知による実装となります。 そして、具体的な実装方法として、スレッドを使って同期的に待つ方法とイベント通知でコールバックさせて非同期に待つ方法の2種類に別れます。前者はシーケンスを記述しやすいですが排他制御の問題が出やすく難易度が高いです。後者はシーケンス制御しようとするとコールバック地獄と呼ばれる地獄に陥りやすいです。 単純にこれらの組合せで4種類のイベント待ち方式が考えられます。 Windowsアプリの場合もこの4種類全てが考えられます。 ポーリングは性能が問題にならない場面に限った方がよいだろうと思います。 今は結構技術が進んでいるので、多くの場合自然にイベント通知方式で実装することになりますし。 「A要求→OK待ち → B要求→OK待ち → C要求→OK待ち → ・・・」のようなシーケンス的に待ちが発生する場合はスレッドを使った方が生産性は上がりやすいです。ただし、排他制御するスキルが必要になります。 「要求→OK待ち」で処理が完了するような時はコールバックによる非同期処理の方が排他制御の問題がでないので安心です。 C#はasync/awaitと言う新しい非同期処理方式があります。比較的お手軽に前者を実装できます。 でも、スレッドを使うと言う事実は同じですので排他制御についはて注意が必要です。 > > シリアルポートに限らず、イベントというのは絶対的に信頼していいものなのでしょうか > > > そのシステム次第です。 イベント・システムを取りこぼしがないよう設計し、適切に実装されていれば取りこぼしはないでしょう。 しかし、そうではない場合は取りこぼしは発生します。Windowsでマウス・ボタンが押されたままになった経験はないでしょうか? これはWM_LBUTTONUP関連イベントの取りこぼしと思われます。(どの層で取りこぼしているのか知りませんが、Windows 10になってから酷くなった気がします。) ボタンは取りこぼしが発生しやすいような気がします。連打されることを考えると寧ろある程度は取りこぼした方が却って良かったりします。 通信は取りこぼし自体は発生しないよう設計されているケースがほとんどと思いますが、ノイズ等によるデータ破壊を考慮し、タイムアウトなどの対策を入れるのが普通です。