为什么我的线程不立即启动?

时间:2010-10-04 16:01:06

标签: c# .net vb.net winforms multithreading

见下面的程序。 我用函数abc启动一个新的线程x,然后我做了一些更长的任务。 为什么x只在end sub之后开始?它不应该在睡觉前立即开始吗?

 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



修改
解决方案是:

(A)将连接尝试和超时倒计时都放入不同的线程中 (B)像这样更新UI:

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

使用全局声明,因此不需要传递参数:

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

4 个答案:

答案 0 :(得分:9)

abc函数确实会在Button1_Click方法结束之前启动。造成混乱的原因是两件事

首先,您将使用以下行直接从后台线程更新UI

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

此代码不正确,可能会在以后导致问题。您必须使用Invoke调用才能实际更改UI。正如您在第二行中所做的那样,它将我们带到下一个问题。

Invoke来电是同步的。它主要通过将消息推送到Windows消息队列并在返回之前等待处理它来运行。您在主线程中添加的Thread.Sleep调用会阻止消息队列实际运行。这有效地停止后台线程,直到Sleep调用完成,从而给出后台线程未运行的外观。

答案 1 :(得分:2)

您正在从非UI线程更新UI。这是不允许的,并且可能导致奇怪的行为。我不知道这是不是导致你的延迟行为,但你需要先解决这个问题。

答案 2 :(得分:0)

要添加其他人的说法,您可以创建一个委托来定义控件更新要求的签名(在这种情况下,我假设文本框和字符串,但可以根据需要定义),然后创建一个遵循该委托签名的方法,如果需要,它会以异步方式递归调用自身。

这样的事情:

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;
}

如果从“正确”线程调用它(因为InvokeRequired在拥有控件的线程调用它时将为false),只需更新文本,或者从“错误”线程调用(因为InvokeRequired将是如果从另一个线程调用,则为true,通过排队由UI线程执行的调用。

答案 3 :(得分:0)

可能与您的问题无关,但是为了从单独的线程更新UI,我建议您使用BackgroundWorker。将abc()函数放在DoWork中,并使用ProgessChanged更新UI。