views:

131

answers:

4

Hi, see below program. I start a new thread x with function abc, then I do some longer task. Why does x only start after end sub? Shouldn't it start right-away, before sleep ?

 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim x As New Threading.Thread(AddressOf abc)
        x.SetApartmentState(Threading.ApartmentState.MTA)
        x.Start()

        System.Threading.Thread.Sleep(5000)
    End Sub





Sub abc()
    For i As Integer = 0 To 10 Step 1
        Me.lblStatus.Text = "Testing DB connection ( timeout in: " + i.ToString() + "s )"
        'Me.StatusStrip1.Invoke(
        MsgBox(i.ToString)
        System.Threading.Thread.Sleep(1000)
    Next
End Sub



Edit:
The solution is this:

(A) Put both the connection attempts and the timeout countdown into separate threads.
(B) Update the UI like this:

    If Me.InvokeRequired Then
        Me.Invoke(pUpdateStatusMessage, "Successfully connected.")
    Else
        UpdateStatusMessage("Successfully connected.")
    End If

With this globally declared, so no argument passing is necessary:

Delegate Sub t_pUpdateStatusText(ByVal strMessage As String)
Public pUpdateStatusMessage As t_pUpdateStatusText = New t_pUpdateStatusText(AddressOf UpdateStatusMessage)

Public Sub UpdateStatusMessage(ByVal strMessage As String)
    Me.lblStatus.Text = strMessage
    Me.StatusStrip1.Update()
End Sub
+2  A: 

You're updating your UI from a non-UI thread. This is not allowed, and can lead to odd behavior. I don't know if that's what's causing your delay behavior, but you'll need to solve that first.

Mark
+8  A: 

The abc function will indeed start before the Button1_Click method ends. What's causing the confusion is 2 things

The first is that you are directly updating the UI from a background thread with the following line

Me.lblStatus.Text = "Testing DB connection ( timeout in: " + i.ToString() + "s )" 

This code is incorrect and can cause issues later on. You must use the Invoke call in order to actually change the UI. As you do in the second line which brings us to the next problem.

The Invoke call is synchronous. It runs by essentially pushing a message onto the windows message queue and waiting for it to be processed before returning. The Thread.Sleep call you added in the main thread prevents the message queue from actually running. This effectively stalls the background thread until the Sleep call completes giving the appearance that the background thread is not running.

JaredPar
A: 

To add to what others are saying, you can create a delegate to define the signature of your control update requirements (in this case, I'm assuming textbox and string, but that can be defined however you need), and then create a method that follows that delegate signature which recursively invokes itself asynchronously if need be.

Something like this:

private delegate void ControlUpdateTextHandler(TextBox ctrl, string text);
public void UpdateControlText(TextBox ctrl, string text)
{
    if (ctrl.InvokeRequired)
    {
        ctrl.BeginInvoke((ControlUpdateTextHandler)UpdateControlText, ctrl, text);
    }
    else
        ctrl.Text = text;
}

Should work if it's called from the "correct" thread (because InvokeRequired will be false if this is called by the thread that owns the control) by simply updating the text, or from the "incorrect" thread (becasue InvokeRequired will be true if called from a different thread), by queuing up the invocation to be performed by the UI thread.

Steven
A: 

Maybe not related to your question,but for updating the UI from a separate thread, I suggest you use the BackgroundWorker. place the abc() function in DoWork and use ProgessChanged to update the UI.

sh_kamalh