我有一个系统托盘应用程序。托盘应用程序有一个图标和一个带有一些选项的上下文菜单。有一个名为 status 的菜单,其中包含以下工具条菜单项:
- 开始
- 重新开始
- 停止
它们根据某些条件启用/禁用。
我的系统托盘应用程序有一个后台线程,它会不断检查一些条件并做一些工作。它的主循环如下:
Do Until gAppExit Or Me.thdExit
' Check some conditions and do some work
Loop
gAppExit 是一个全局变量,指示用户是否已通过“退出”工具条菜单项退出应用程序。
thdExit 表示线程是否应该退出循环(我稍后解释)。
当用户想要重新启动后台线程时,他单击重新启动工具条菜单并完成以下顺序(重新启动->暂停->等待完成):
Public Function ReStart() As Integer
Dim result As Integer
If IsNothing(ThreadBgWorker) Then
Logger.Write("Task is not yet created!", LOGGING_CRITICAL_ERRORS_CATEGORY)
Return RESULT_ERROR
End If
result = Me.Halt()
If result <> RESULT_ERROR Then
result = Me.Start()
End If
Return result
End Function
Public Function Halt() As Integer
Dim result As Integer
If IsNothing(ThreadBgWorker) Then
Logger.Write("Task is not yet created!", LOGGING_CRITICAL_ERRORS_CATEGORY)
Return RESULT_ERROR
End If
Me.thdExit = True
result = Me.WaitFinish()
Return result
End Function
Public Function WaitFinish() As Integer
Dim result As Integer
If IsNothing(ThreadBgWorker) Then
Return RESULT_ERROR
End If
result = RESULT_ERROR
Try
'TODO:
Console.WriteLine("Wait thread to finish (before Join)...")
Me.ThreadBgWorker.Join()
'TODO:
Console.WriteLine("Thread finished... Continue restarting thread...")
Me.RetVal = True
result = Me.ThreadBgWorker.ManagedThreadId
Logger.Write(String.Format("Task ""{0}"" correctly stopped.", _
Me.ThreadBgWorker.Name))
Catch ex As Exception
Logger.Write(String.Format("Couldn't stop task ""{0}"": {1}", _
Me.ThreadBgWorker.Name, ex.Message), _
LOGGING_CRITICAL_ERRORS_CATEGORY)
End Try
Return result
End Function
Public Function Start() As Integer
Dim result As Integer
If IsNothing(ThreadBgWorker) Then
Logger.Write("Task is not yet created!", LOGGING_CRITICAL_ERRORS_CATEGORY)
Return RESULT_ERROR
End If
result = RESULT_ERROR
Me.thdExit = False
Try
If Me.ThreadBgWorker.ThreadState = Threading.ThreadState.Stopped Then
Me.Create()
End If
Me.ThreadBgWorker.Start() ' Start the new thread.
result = Me.ThreadBgWorker.ManagedThreadId
Logger.Write(String.Format("Task ""{0}"" correctly started.", _
Me.ThreadBgWorker.Name))
Catch ex As Exception
Logger.Write(String.Format("Couldn't start task ""{0}"": {1}", _
Me.ThreadBgWorker.Name, ex.Message), _
LOGGING_CRITICAL_ERRORS_CATEGORY)
End Try
Return result
End Function
请注意,在 Halt 函数上,它通过在函数 WaitFinish 上调用 Me.ThreadBgWorker.Join() 来等待线程完成。在调用 WaitFinish 函数之前,将 thdExit 设置为 true,以便后台线程可以退出主循环:
Do Until gAppExit Or Me.thdExit
' Check some conditions and do some work
Loop
ChangeStatusToStopped()
在退出循环时,调用 ChangeStatusToStopped(),如下所示:
Private Delegate Sub ChangeStatusToStoppedDelegate()
Public Sub ChangeStatusToStopped()
' TODO:
Console.WriteLine("Changing status to stopped...")
System.Windows.Forms.Application.DoEvents()
If MainMenu.InvokeRequired Then
'TODO:
Console.WriteLine("Invoke required!")
MainMenu.Invoke(New ChangeStatusToStoppedDelegate(AddressOf ChangeStatusToStopped))
Else
'TODO:
Console.WriteLine("Invoke NOT required!")
Me.submnuStart.Enabled = True
Me.submnuReStart.Enabled = False
Me.submnuStop.Enabled = False
End If
' TODO:
Console.WriteLine("Status changed to stopped.")
End Sub
它的作用是在 UI 中启用启动工具条菜单项并禁用重新启动和停止工具条菜单项。
问题是:
在 ChangeStatusToStopped 方法中,当 MainMenu.InvokeRequired 为 true 时,它调用:
MainMenu.Invoke(New ChangeStatusToStoppedDelegate(AddressOf ChangeStatusToStopped))
然后它就卡在那里了,也就是说,else body:
Me.submnuStart.Enabled = True
Me.submnuReStart.Enabled = False
Me.submnuStop.Enabled = False
永远不会被执行。似乎主线程很忙或消息泵中的其他问题....有什么想法吗?
我看到了那条线:
Me.ThreadBgWorker.Join()
在后台线程退出主循环之前到达 WaitFinish() 函数,尽管在执行 Me.ThreadBgWorker.Join() 之前已将 thdExit 设置为 true,但一旦执行连接,应用程序就会卡住,后台线程无法退出主循环(似乎应用程序是忙或冻结)。