Cross-Thread operation

ibigpapa

New member
Joined
Jan 4, 2008
Messages
4
Programming Experience
1-3
So I'm trying to update a text box, I start a thread with the thread.start() method, The thread runs a class method. I have it raise an event at a certain location with a string of information, in the main form I handle the event, it takes the string and adds to a text box. I get error stating this is not thread safe, I've looked at the background worker, but I am still unable to see how to apply it to my code.

So if someone could show me how to turn this into a backgroundworker or how to make this thread safe that would be great. Thank you in advance

the code to create thread in the main form
VB.NET:
Public Class form1

     Dim WithEvents oClass as testClass
     Dim oThread as Thread

     Sub button1_click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click

          oClass = New testClass()
          oThread = New Thread(AddressOf oClass.start)
          oThread.Start()

     end sub

     Public Sub oClassResultEvent(ByVal strResult As String) Handles oClass.classResult
          textbox1.Text &= strResult & ControlChars.NewLine
     End Sub
End Class

Code of the class
VB.NET:
Public Class testClass
        Public Sub start()
              For I as Integer = 1 to 100
                   RaiseEvent classResult(i)
              Next
        End Sub
        Public Event classResult(ByVal strResult as String)
End Class
 
Last edited:
Add BackgroundWorker from Toolbox to form. Set WorkerReportsProgress to True. Doubleclick the component and DoWork event handler is generated, add this code:
VB.NET:
For I As Integer = 1 To 1000
    BackgroundWorker1.ReportProgress(0, I.ToString)
Next
Select the ProgressChanged event and add this code to the generated handler method:
VB.NET:
Label1.Text = e.UserState
Start the BackgroundWorker with RunWorkerAsync method.
 
Thank you very much

Thanks for that it makes perfect sense now. Is there a way to just have it run the class method in the dowork or does that have to be coded in?

or should i just make the class create the background worker and change the start method to the do work method?
 
VB.NET:
    Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        For I As Integer = 1 To 1000
            BackgroundWorker1.ReportProgress(0, I.ToString)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.WorkerReportsProgress = True
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = e.UserState
    End Sub

All I get in Label1 is 1000 - nothing before that. Is that correct - my expectation was that I would see it incrementing :( Did I miss something out?
 
VB.NET:
 Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = e.UserState
    End Sub

change so the label text value not overwritten or add a delay, The reason is because the computer can go from 1 to 1000 very fast. After testing it seems you need to have a delay in either occasion so keep the do work method same in both ways shown

Delay method
VB.NET:
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        For I As Integer = 1 To 1000
            BackgroundWorker1.ReportProgress(0, I.ToString)
            System.Threading.Thread.Sleep(600);
        Next
    End Sub

 Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = e.UserState
    End Sub

Highest number on top method
VB.NET:
 Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = e.UserState & Controlchars.Newline & Label1.Text
    End Sub


Now back to my other question is it possible to make the do work method use another method without having to put the whole method in there like addressof or somethign similar?
 
Last edited:
You can call any method from the DoWork method. For example pseudo:
VB.NET:
sub bw_DoWork()
  dim c as new classy
  c.worker = BackgroundWorker1 'to let it use the reportprogress method
  c.workit()
end sub
You can also use a BackgroundWorker inside a class. (that is what the form example does)
 
Figured it out

Thank you very much JohnH, as you made it so i understood enough to sit down and think about it. I did manage to get it to work with my class.

Thank you InertiaM for replying and making me sit down and think.

so the final code

VB.NET:
Public Class form1

      Dim WithEvents oClass as testClass
      Dim oThread as Thread

      Sub button1_click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click

          BackgroundWorker1.RunWorkerAsync()
          
      end sub

      Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

        oClass = New testClass
        oClass.start()

     End Sub

     Public Sub oClassResultEvent(ByVal strResult As String) Handles oClass.classResult

          BackgroundWorker1.ReportProgress(0, strResult)

     End Sub

     Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
          TextBox1.Text = e.UserState & ControlChars.NewLine & TextBox1.Text
     End Sub

End Class

Code of the class
VB.NET:
Public Class testClass
        Public Sub start()
              For I as Integer = 1 to 100
                   RaiseEvent classResult(i)
                   System.Theading.Thread.Sleep(600)
              Next
        End Sub
        Public Event classResult(ByVal strResult as String)
End Class
 
OK, I think I'm following :)

If I now need to cancel it running before it finished, how can I do it safely so that I could start it again. Everything I've tried so far returns "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently" :(
 
You must set WorkerSupportsCancellation to True, and during the DoWork you must periodically check the CancellationPending property to see if you need to stop. Calling the CancelAsync method only sets CancellationPending property to True. If you detect and abort work in DoWork method you can set e.Cancel=True and this info can be read also in RunWorkerCompleted event handler.

Before calling the RunWorkerAsync method you should check the IsBusy property.
 
Back
Top