画像ファイル
画像ファイル RS-232C マルチスレッドの壁
RS-232C関係
1RS-232Cの基礎
2RS-232Cの何が変わった..
3SerialPortクラス
4Unicode(ユニコード)の壁
5マルチスレッドの壁
6RS-232C サンプルコード
7RS-232CのHEXモニタ
8RS-232C 送信モジュール
9RS-232Cのループテスト
10RS-232Cのピンチェンジ..

VB.NET C#全般
1羊の皮を着た狼 VB.NET
2Form1、Form2の相互参照
3Form1、Form2の相互参照 2
4VB.NET C# データ型の基本
5VB.NET C# 文字列
6VB.NET タイマー精度
7BackgroundWorkerの魅力1..
8BackgroundWorkerの魅力2..
9VB6のタイマー
10コントロールの配列をインデクサ..
11コントロールの配列はジェネリク..
12インデクサ(C#、VB.NET)
13インデクサでBit操作
14Unicode 入門
15デリゲート入門
16マルチスレッド入門
17イベント入門
18デリゲートとイベント
18インターフェースの基本

RichTextBox関係
1RichTextBoxの不思議
2テキスト色付け高速化計画
3VB.NET RichTextBox1
4VB.NET RichTextBox 2

Socket通信
1C#、VB2005 でSocket通信
2サーバー 複数接続

プロセス間通信
1プロセス間通信(送信側)
2プロセス間通信(受信側)


質問、意見はこちらに
画像ファイル


マルチスレッドを克服せよ
System.IO.Ports.SerialPortクラスのイベントは全て別スレッドで発生する。
従ってSerialPortクラスの受信は第2スレッドで処理することとなる。
ここでは今までのMSCOMとSystem.IO.Ports.SerialPortクラスの受信の違いを説明しよう。

失敗するコード
System.IO.Ports.SerialPortクラスでデーターを受信する場合は、DataReceivedイベントを使う。
これは受信バッファに設定のスレッシュホールド以上のデーターが入った場イベントが発生する。
データの受信は以下のコードを試してみた。
これはDataReceivedイベントで受信データーを取得し、sift-jisからunicodeに変換して、
フォーム上のテキストボックスtxtReceiveにテキストデーターを表示させようとするものである。
データーの受信 コード
  'データーの受信
 Private Sub DataReceived(ByVal sender As Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles SerialPort1.DataReceived
Dim byteArray(SerialPort1.BytesToRead - 1) As Byte
'unicodeのEcodingクラスに作成
Dim uni As Encoding = Encoding.Unicode
's-jisのEncodingクラスの作成
Dim ecSjis As Encoding = Encoding.GetEncoding("shift-jis")
  'ポートからのバッファから読み込み
SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead)
Dim uniBytes As Byte() = Encoding.Convert(ecSjis, uni, byteArray)
'unicodeのバイト配列からテキストに変換
txtReceive.Text = uni.GetString(uniBytes)
 End Sub

失敗の原因は
しかしながらこのコードは見事に失敗する、
"有効ではないスレッド間の操作: コントロールが作成されたスレッド以外の
スレッドからコントロール'txtReceive' がアクセスされました。"

このような見慣れない、エラーメッセージが出てプログラムが中断されてしまうのだ。
MSDNでDataReceived イベントはの説明を見ると、
・DataReceived イベントは、データが SerialPort オブジェクトから受信されたときに
2次スレッドで発生します。

この2次スレッドとは何で有ろうか?
「RS-232Cの受信はマルチスレッドで行われる」のである。
ここでマルチスレッドに関して簡単に説明しよう。
今あなたが、大きなファイルを読み込んでそのデーターを画面上に表示するとしよう。
大きなファイルを読み込みはじめると、コンピューターは他の操作を受け付けなくなる、
もしファイルを読み込み始めて、実は間違ったファイルであることに気付いてStopボタンを
押したとしよう、しかしコンピューターは通常ストップのボタンのイベントを処理しない、 忙しいからだ。
この処理をさせようとすると、読み込みファイルのループの中にApplication.DoEventsを
入れる必要が有る。
このコマンドは、Windowsに対してそのメッセージループに溜まっているメッセージを
処理するように促すのである。
しかしながら、ループを使用しない読み込みの場合はいかがであろうか、当然お手上げである。
マルチスレッドは複数の処理が並列に走る方式である、これを使えば有る処理中に別な処理が
可能となる、今までのVBでは出来なかった、夢の機能なのだ。
ただしこの方法も便利な分だけ制約も存在する、マルチスレッドは同じ変数や、
コントロールのインスタンスを共用する。
もし2つのスレッドが同時に矛盾するアクセスをしたらどうなるだろうか。
ここではマルチスレッドの説明がメインのテーマでは無いので詳しい説明は割愛するが、
VB.NETにはスレッドセーフなアクセスを求める、たとえばフォーム上のコントロール
にアクセスする場合など、そのコントロールを作成したスレッドから呼び出す必要が有るのだ。
この方法はデリゲートとInvokeと言うメソドを使用する。

失敗しないコード マルチスレッドによる受信
VB2005のコード
    '*******************************************
' //////受信//////
'*******************************************
'受信も送信と同様にs-Jisで行う
'デリゲートの宣言
Public Delegate Sub myDelegate()
'データーの受信
    Private Sub DataReceived(ByVal sender As Object, _
          ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
          Handles SerialPort1.DataReceived
'データーの受信はマルチスレッドで行われる為にデリゲートを使用して
'メインのスレッドでデーターの表示を行う必要がある。
txtReceive.Invoke(New _
myDelegate(AddressOf updateTextBox), _
New Object() {})
End Sub
'--------------------------------------
'受信データーをテキストボックスに書き込む。
'--------------------------------------
Public Sub updateTextBox()
'unicodeのEcodingクラスに作成
Dim uni As Encoding = Encoding.Unicode
's-jisのEncodingクラスの作成
Dim ecSjis As Encoding = Encoding.GetEncoding("shift-jis")
'----------------------------------------------------
'データーの読み込み
'232cの読み込むByteの配列を作成
    'serialPort1.BytesToReadは受信データーのバッファサイズ
'----------------------------------------------------
Dim byteArray(SerialPort1.BytesToRead - 1) As Byte
'ポートのバッファから読み込み
SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead)
'*****文字変換****
'sift-jisからunicodeへの変換
Dim uniBytes As Byte() = Encoding.Convert(ecSjis, uni, byteArray)
'unicodeのバイト配列からテキストに変換
Dim strGetText As String = uni.GetString(uniBytes)
'****受信データーをテキストボックスに追加する****
txtReceive.AppendText(strGetText)
End Sub
上がその修正版である、DelegateとInvokeを使用して、データー受信の別スレッドから
メインスレッドの txtReceiveに文字を追加しているのである。
注目してほしいのはDataReceivedイベントの中の、
デリゲートサンプルコード
VB2005のコード
 txtReceive.Invoke(New _
myDelegate(AddressOf updateTextBox), _
New Object() {})
ここでtxtReceiveはフォーム上に置かれた受信データーを表示する、テキストボックスである。
Invokeはコントロールクラスのメソドである、Invokeを使用するとそのコントロールが
作成されたスレッドでの処理が可能となるのである。

マルチスレッドに関しては 「マルチスレッド入門」 を参照して下さい。

(注意)
この記事はFramework1.0を使用時に書かれています。
Framework2.0以降はマルチスレッド内でのブレークによるデバッグが出来るようになりました。
これはかなりの進歩で、マルチスレッドを使う大きな障害が無くなったのです。
今まではデバッグの為にメインスレッドで書かれた時間のかかる処理は、全て別スレッドで行なうことが良いでしょう。
更に上のコードは受信イベントが2度に分けて行なわれた場合、Byteの区切りにより文字化けが発生します。
この対策として、文字列の受信に関しては改行を文字列の区切りとして認識する方法のサンプルが、
「VB2005 RS-232C サンプルコード」
「C#,VB2005でRS-232Cのループテスト 」
に書かれていますので、受信処理はそちらも参照して下さい。

画像ファイル