见下面的程序。 我用函数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
答案 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。