Threading Question

B

Bill Schanks

I have a form that gets opened, and it a separate thread I want a
timer at the bottom of the form to update while the datagridview is
updating.

In my main form I have this:
Dim t As New Thread(AddressOf ChildForm.UpdateRetrieveTimer)
t.Start()
<<Snip>>

Here is the code for the thread:
Public Sub UpdateRetrieveTimer()
Dim ts As TimeSpan
Dim s As String
Do While Not g_bRetreiveDone(g_ChildFormNumber)
ts = Date.Now - g_dtStartQuery
s = String.Format("{0:d2}:{1:d2}:{2:d2}", ts.Hours,
ts.Minutes, ts.Seconds)
Me.rssRetrieveTime.Text = s
Loop
End Sub

The problem is on the line 'Me.rssRetrieveTime.Text = s'. It pauses
here.
 
B

Bill Schanks

I have been trying to get the backgroundworker to work. Here is what I
have. The problem is the .dowork section runs for 5 seconds or so but
the datagridview doesn't have any results on it.

Private WithEvents ReportWorker As
System.ComponentModel.BackgroundWorker

<<snip>>
'Get the results (Background worker)
_dtStartQuery = Date.Now
ReportWorker = New System.ComponentModel.BackgroundWorker
ReportWorker.WorkerReportsProgress = True
ReportWorker.RunWorkerAsync()
_ChildForm.tsBtnStop.Enabled = True

'Show timer while data is retrieved
While ReportWorker.IsBusy
_ts = Date.Now - _dtStartQuery
_ChildForm.rssRetrieveTime.Text =
String.Format("{0:d2}:{1:d2}:{2:d2}.{3:d3}", _
_ts.Hours, _ts.Minutes, _ts.Seconds,
_ts.Milliseconds)
Application.DoEvents()
End While
<<snip>>

Private Sub ReportWorker_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles ReportWorker.DoWork


_ChildForm.Spoc_ev_resultsTableAdapter.Fill(_ChildForm.RCT_EV_QADataSet.spoc_ev_results,
_
_dtAsOf, _
_sRptID, _
_sDB, _
_sBL, _
_sCID, _
_sMgr, _
_bCommentsMissing)

End Sub
 
B

Branco Medeiros

Hi, Bill.

It seems you're doing the same thing again (i. e., updating a control
in a background thead), only now it's the Datagrid.

I suspect that when you call
_ChildForm.Spoc_ev_resultsTableAdapter.Fill(...) from inside the
DoWork method, the update gets blocked, 'cause anything running inside
the DoWork method is in a secondary thread.

As Tom suggested, one way to do what you want could be to use the
ProgressChanged event of the BackgroundWorker (which kicks in on the
main thread) to update the display. In this case, the filling of the
grid should be left in the main thread (as you were originally doing
it seems).

Another approach, as Tom also pointed out, is to use the form's invoke
method. It turns out a control (and the form) has the method
InvokeRequired which returns true if the current thread can't update
the user interface. If so, then you must resort to calling the form's
Invoke method, passing the address of the method to execute and an
array with the method's parameters. Invoke will then ensure that the
method is executed in a thread that can update the UI (usually the
main thread).

As a side note, considering your first example, it seems also you are
acessing, from your secondary thread, a variable which gets data
updated from the main thread (g_bRetreiveDone(g_ChildFormNumber)).
Ideally, all access to variables shared between threads should be
"interlocked". In your case, maybe you don't get into trouble, cause
it seems it's only the main thread that updates the variable, but keep
this in mind for future reference.

Finally, notice that you have a **very tight** loop running inside
UpdateRetrievetimer. I mean, the thing is running *non-stop* probably
hundreds or even thousands of times per second without much need,
since its only job is to update the screen once per second. It would
be nice if you put it to rest at regular intervals, say 500 ms...

Using your original approach as reference, here's a possible (but not
tested, mind you) solution:

<example>
'In the child form
Delegate Sub Updater(Text As String)


Private mFinishUpdate As Boolean
'used to lock shared access to the variable above
Private mLock As New Object

Private Property Finished As Boolean
Get
Dim Result As Boolean
SyncLock(mLock)
Result = mFinished
End SyncLock
Return Result
End Get
Set(Value As Boolean)
SyncLock(mLock)
mFinished = Value
End SyncLock
End Set
End Property

Sub UpdateTimer(Text As String)
Static DoUpdate As Updater = AddressOf UpdateTimer
If Me.InvokeRequired Then
'this runs in the secondary thread
Me.Invoke(DoUpdate, New Object(){Text})
Else
'this runs in the main thread
Me.rssRetrieveTime.Text = Text
End If
End Sub

Public Sub UpdateRetrieveTimer()
Dim ts As TimeSpan
Dim s As String
Do While Not Finished
ts = Date.Now - g_dtStartQuery
s = String.Format("{0:d2}:{1:d2}:{2:d2}", _
ts.Hours, ts.Minutes, ts.Seconds)
UpdateTimer(s)
System.Threading.Thread.Sleep(500)
Loop
End Sub
</example>

Hope this helps.

Regards,

Branco.

------------------
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top