| RS-232C マルチスレッドの壁 |
|
マルチスレッドを克服せよ
System.IO.Ports.SerialPortクラスのイベントは全て別スレッドで発生する。
従ってSerialPortクラスの受信は第2スレッドで処理することとなる。 ここでは今までのMSCOMとSystem.IO.Ports.SerialPortクラスの受信の違いを説明しよう。
失敗するコード
System.IO.Ports.SerialPortクラスでデーターを受信する場合は、DataReceivedイベントを使う。
これは受信バッファに設定のスレッシュホールド以上のデーターが入った場イベントが発生する。 データの受信は以下のコードを試してみた。 これはDataReceivedイベントで受信データーを取得し、sift-jisからunicodeに変換して、 フォーム上のテキストボックスtxtReceiveにテキストデーターを表示させようとするものである。
データーの受信 コード
'データーの受信
失敗の原因は
しかしながらこのコードは見事に失敗する、
"有効ではないスレッド間の操作: コントロールが作成されたスレッド以外の スレッドからコントロール'txtReceive' がアクセスされました。" このような見慣れない、エラーメッセージが出てプログラムが中断されてしまうのだ。 MSDNでDataReceived イベントはの説明を見ると、 ・DataReceived イベントは、データが SerialPort オブジェクトから受信されたときに 2次スレッドで発生します。 この2次スレッドとは何で有ろうか? 「RS-232Cの受信はマルチスレッドで行われる」のである。 ここでマルチスレッドに関して簡単に説明しよう。 今あなたが、大きなファイルを読み込んでそのデーターを画面上に表示するとしよう。 大きなファイルを読み込みはじめると、コンピューターは他の操作を受け付けなくなる、 もしファイルを読み込み始めて、実は間違ったファイルであることに気付いてStopボタンを 押したとしよう、しかしコンピューターは通常ストップのボタンのイベントを処理しない、 忙しいからだ。 この処理をさせようとすると、読み込みファイルのループの中にApplication.DoEventsを 入れる必要が有る。 このコマンドは、Windowsに対してそのメッセージループに溜まっているメッセージを 処理するように促すのである。 しかしながら、ループを使用しない読み込みの場合はいかがであろうか、当然お手上げである。 マルチスレッドは複数の処理が並列に走る方式である、これを使えば有る処理中に別な処理が 可能となる、今までのVBでは出来なかった、夢の機能なのだ。 ただしこの方法も便利な分だけ制約も存在する、マルチスレッドは同じ変数や、 コントロールのインスタンスを共用する。 もし2つのスレッドが同時に矛盾するアクセスをしたらどうなるだろうか。 ここではマルチスレッドの説明がメインのテーマでは無いので詳しい説明は割愛するが、 VB.NETにはスレッドセーフなアクセスを求める、たとえばフォーム上のコントロール にアクセスする場合など、そのコントロールを作成したスレッドから呼び出す必要が有るのだ。 この方法はデリゲートとInvokeと言うメソドを使用する。
失敗しないコード マルチスレッドによる受信
VB2005のコード
'*******************************************
上がその修正版である、DelegateとInvokeを使用して、データー受信の別スレッドから
メインスレッドの txtReceiveに文字を追加しているのである。 注目してほしいのはDataReceivedイベントの中の、
デリゲートサンプルコード
VB2005のコード
txtReceive.Invoke(New _
ここでtxtReceiveはフォーム上に置かれた受信データーを表示する、テキストボックスである。
Invokeはコントロールクラスのメソドである、Invokeを使用するとそのコントロールが 作成されたスレッドでの処理が可能となるのである。 マルチスレッドに関しては 「マルチスレッド入門」 を参照して下さい。
(注意)
この記事はFramework1.0を使用時に書かれています。 Framework2.0以降はマルチスレッド内でのブレークによるデバッグが出来るようになりました。 これはかなりの進歩で、マルチスレッドを使う大きな障害が無くなったのです。 今まではデバッグの為にメインスレッドで書かれた時間のかかる処理は、全て別スレッドで行なうことが良いでしょう。 更に上のコードは受信イベントが2度に分けて行なわれた場合、Byteの区切りにより文字化けが発生します。 この対策として、文字列の受信に関しては改行を文字列の区切りとして認識する方法のサンプルが、 「VB2005 RS-232C サンプルコード」 「C#,VB2005でRS-232Cのループテスト 」 に書かれていますので、受信処理はそちらも参照して下さい。 |