「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール・・・」とエラーが出て対処法がわからずに困って降ります。
流れとしては、
クラスを作成してそこに別スレッドで1秒毎にイベントを発生させています。
この発生したイベントをメインスレッドのイベントとして使用したいのです。
どうすれば良いのか解からず困っています。
どうか、御教授願えませんでしょうか。
Imports System
Imports System.Threading
Public Class Class1
Public Event GetMow(ByVal sender As Object, ByVal e As EventArgs)
Public Sub Run()
Dim timerDelegate As TimerCallback _
= New TimerCallback(AddressOf MyClock)
Dim timer As Timer _
= New Timer(timerDelegate, Nothing, 0, 1000)
Console.ReadLine() ' キーが押されるまで待機
End Sub
Public Sub MyClock(o As Object)
Console.WriteLine(DateTime.Now)
RaiseEvent GetMow(Me, New EventArgs)
End Sub
End Class
Public Class form1
Dim WithEvents ss As New Class1
Private Sub ss_GetMow(ByVal sender As Object, ByVal e As System.EventArgs) Handles ss.GetMow
ProgramTimer.Label1.Text = Now.Minute
End Sub
Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
ss.Run()
End Sub
End Class
よろしくお願いいたします。
> エラーが出て対処法がわからずに困って降ります。
コントロール(Form、TextBox、Label など)を、別スレッドから
操作することはできません。UI スレッドで生成されたコントロールは、
UI スレッドからのみ読み書きが可能です。
> 別スレッドで1秒毎にイベントを発生させています。
System.Windows.Forms.Timer では駄目なのでしょうか。
> この発生したイベントをメインスレッドのイベントとして使用したいのです。
慣れないうちは、BackgroundWorker を使うと分かりやすいかも。
ワーカースレッドから ReportProgress メソッドでイベントを発生させ、
画面側は ProgressChanged イベントでそれを受け取るようにします。
> どうすれば良いのか解からず困っています。
http://www.atmarkit.co.jp/fdotnet/dotnettips/312ctrlinvoke/ctrlinvoke.html
http://www.atmarkit.co.jp/fdotnet/dotnettips/373threadtimer/threadtimer.html
> どうか、御教授願えませんでしょうか。
http://www.tt.rim.or.jp/~rudyard/torii009.html
http://blogs.wankuma.com/jeanne/archive/2005/11/24/19566.aspx
ご指摘有難うございます。
BackgroundWorkerを使用したのですが、Label1には何も表示されません。
BackgroundWorker1_ProgressChangedは別スレッド扱いなのでしょうか。
Imports System
Imports System.Threading
Public Class Form1
Dim ss As New Class1
Private Sub form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
ss.Run()
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_ProgressChanged _
(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) _
Handles BackgroundWorker1.ProgressChanged
Debug.Print(e.ProgressPercentage)
Label1.Text = e.ProgressPercentage
End Sub
End Class
Public Class Class1
Public Sub Run()
Dim timerDelegate As TimerCallback _
= New TimerCallback(AddressOf MyClock)
Dim timer As Timer _
= New Timer(timerDelegate, Nothing, 0, 1000)
End Sub
Public Sub MyClock(o As Object)
Form1.BackgroundWorker1.WorkerReportsProgress = True
Form1.BackgroundWorker1.ReportProgress(CInt(DateTime.Now.Second))
End Sub
End Class
> BackgroundWorkerを使用したのですが、Label1には何も表示されません。
使い方が間違っています。
BackgroundWorker を使うなら、RunWorkerAsync メソッドから
実行されるための「DoWork イベント」の記述が必要です。
今回のコードでは、Form1 に DoWork イベントの処理が記載されて
いないため、ワーカースレッドは「何もせずに即座に終了」しています。
この場合、「BackgroundWorker のワーカースレッド」と
「UI スレッド」と「TimerCallback のスレッド」が
それぞれ別物であることに注意してください。
スレッド管理を BackgroundWorker に任せたのであれば、
本来は TimerCallback の出番はありません。
今回のサンプルであれば、DoWork イベントの中で
「1000ミリ秒 Sleep させてから、ReportProgress を呼ぶ」ことで、
処理できるでしょう。それがやりたいことに合致しているかは別として。
> BackgroundWorker1_ProgressChangedは別スレッド扱いなのでしょうか。
BackgroundWorker クラスの ProgressChanged イベントは、
「RunWorkerAsync メソッドを呼び出したスレッド」
にて呼び出されます。今回の場合は Form1 の UI スレッドですね。
> Public Sub MyClock(o As Object)
> Form1.BackgroundWorker1.WorkerReportsProgress = True
> Form1.BackgroundWorker1.ReportProgress(CInt(DateTime.Now.Second))
> End Sub
上記のようなことは、絶対に行ってはいけません。
マルチスレッドプログラミングにおいては、操作できるのは基本的に、
自身のスレッド上で動いているオブジェクトのみに限定されます。
不用意に他のスレッドのオブジェクトの状態を変化させてしまうと、
同時実行制御の上で問題を生じ、再現させにくいトラブルを
生じさせてしまう要因となります。
どうしても他のスレッドに進捗情報を伝える必要がある場合には、
以下のようにして対処することができます。
(案1) 相手側のスレッドに対して情報を通知し、実際の処理は
相手のスレッドに処理を「依頼」する形をとる。
→ BackgroundWorker はこの方法をとるために、内部で
AsyncOperationManager / AsyncOperation クラスを利用しています。
BackgroundWorker を用いず、自身で管理しているスレッドから
UI スレッドを操作したいような場合には、前回紹介した URL に
記載されている方法を使うのが良いでしょう。
(案2) フィールド変数やプロパティのアクセスなどは避け、
常に「スレッドセーフな呼び出しが保証されたメソッド」のみを使う。
→ スレッドセーフなコードであれば、複数のスレッドから
同時並行的に実行されても、特に問題は生じません。
(案3) いずれかのスレッドが状態を変更させている間は、
他のスレッドがその変数やプロパティを読み書きしないように
何かしらの同時実行制御や排他制御を施す。
→ ReaderWriterLock や Interlocked などのクラスを通じて
変数を操作するようにするとか、あるいは SyncLock で囲むとか。
# 具体的な手順は、前回紹介した URL をご覧ください。
ツイート | ![]() |