投稿者 みっちー  (社会人) 投稿日時 2009/2/4 09:19:19
はじめまして。みっちーと申します。
早速ですが、質問させてください。
現在仕事で、複数の装置から値を取得してDBに登録したり画面に表示したりするプログラムを作成しています。
使用しているのはVB2005です。
これについて、「一つ以上の装置との接続が切れた場合、接続できる状態になったら自動的に再接続をする」という機能を盛り込まなくてはならなくなりました。
装置との接続や値取得には装置メーカーが提供しているAPIを使用していますが、残念ながら「接続できる状態かどうか」を返してくれるプロパティやメソッドが存在しません。
ですので接続が切れた場合はTimerを使って、一定時間ごとに(必ず)再接続を試みるという仕様にすることになりました。

ところが、一つ問題が発生してしまいました。
BackgroundWorkerを使用しているにも関わらず、接続できない状態の場合、再接続を開始してからタイムアウトまでの間プログラムがフリーズしてしまうのです。
画面の操作ができなくなるのも問題ですが、装置とプログラムは常にお互いに通信をしていて、一定時間(約5秒)反応がないと相手方にエラーが発生していると判断する仕様になっているのです。(生存確認)
(この仕様は変更できません。)

再接続処理で止まっている間に、正常に接続できている装置から、プログラム側にエラーがあると判断されてしまうため、困っています。
マルチスレッドを使用していても、こういった場合は止まってしまうのが普通なのでしょうか?
よくBackgroundWorkerの説明で、Sleepを使ってわざと処理を止めて時間を使い、他のスレッドはそれにかかわらず正常に動く、という例をみるのですが、こういった場合はこれとはまた違うのでしょうか。
それとも何か、使い方や考え方が間違っているのでしょうか。

だいたいのプログラムは以下のようになっています。(直接関係ないところは端折ってあります。)

Private Sub frmMain_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
   '装置接続処理。 
   '接続が確立されるまで生存確認は行わないので、ここで時間がかかるのは問題ありません。 
   '(ゆえにBackgroundWorkerは使用していません) 
End Sub

Private Sub Timer1_Tick(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Timer1.Tick
   Try
      '生存確認 
      For i As Integer = 0 To (装置数)
         '装置がオープンされていない場合、確認ができない(する必要がない)ため飛ばす 
         If isOpen(i) Then '※1 
            Continue For
         End If

         If i = 0 Then
            If (Not _bwLive1.IsBusy()) Then
               bwLive1.RunWorkerAsync()
         End If
         ElseIf i = 1 Then
            If (Not _bwLive2.IsBusy()) Then
               _bwLive2.RunWorkerAsync()
            End If
         End If
         (以下予定している装置数分だけ続きます。)
      Next
      

      '再接続(接続されていない場合は再接続を試みる) 
      For i As Integer = 0 To (装置数)
         '装置がオープンされていれば問題ないので飛ばす 
         If isOpen(i) Then '※1 
            Continue For
         End If

         If i = 0 Then
            If (Not _bwRetry1.IsBusy()) Then
               bwRetry1.RunWorkerAsync()
            End If
         ElseIf i = 1 Then
            If (Not _bwRetry2.IsBusy()) Then
               _bwRetry2.RunWorkerAsync()
            End If
         End If
         (以下予定している装置数分だけ続きます。)
      Next

   Catch
      'エラー時処理 
   End Try

End Sub

'※2 
Private Sub _bwLive1_DoWork(ByVal sender As System.ObjectByVal e As System.ComponentModel.DoWorkEventArgs) Handles _bwLive1.DoWork
   '生存確認の処理 
   '(正常であることを示す値を装置へ書き込みにいく。などの処理) 
End Sub

'※2 
Private Sub _bwRetry1_DoWork(ByVal sender As System.ObjectByVal e As System.ComponentModel.DoWorkEventArgs) Handles _bwRetry1.DoWork
   Try
      deviceReConnect(装置番号) '※3 
      isOpen(0) = True
   Catch ex As Exception
      isOpen(0) = False
      Throw
   End Try
End Sub


※1)この変数はこちらで独自に用意したもので、APIとは無関係です。接続時または再接続時にTrue、切断時または通信エラー時にFalseにするようコーディングしています。
※2)_bwLive2以降、_bwRetry2以降は装置番号が違うだけで、他は1と同じです。
※3)再接続処理。ここで、まだ接続できない状態の場合はタイムアウトまで待たされるため、時間がかかります。
   また、タイムアウトは例外エラーとして処理されるため、「Catch ex As Exception」でキャッチされます。


以上です。
※3の部分でどれだけ時間がかかろうと、生存確認は通常と同じタイミングで実行され、フォームの操作も可能であるというつもりでコーディングしたのですが、実際はそうなっていません。
全体的にフリーズしてしまっているような状態です。
(※3が終われば何事もなかったように正常に動作します。)
「タイムアウトを待っている間にも他のスレッドは進行する」というようにしたいのですが、どうしたらいいのでしょうか。

長々と申し訳ありません。
何かアドバイスがあればよろしくお願いいたします。