我正在开发一个解析器应用程序,用于从已从数据库中提取的DDL文件构建调用树。我们的想法是获取大量这些DDL文件,并确切地确定调用什么。为此,我使用的是.NET TreeView。我正在努力的最终结果是这样的:
-Proc1
-Proc2
-Proc3
-Proc4
-Proc2
-Proc3
-Proc3
-Proc4
现在,我的所有解析都正常工作。虽然,这个过程非常漫长。因此我决定将所有重量级处理移动到自己的线程上。一切都按预期工作,直到我需要更新我的TreeView。我试图将所有实际的更新逻辑保留在单独的线程上,只更新TreeView。但是,尽管我的主要表单是SyncLocked,但在尝试访问树时,我仍然遇到异常。
我在网上找到了很多例子,展示了如何使用Delegates进行线程安全访问,但不幸的是,它们对我的需求来说有点过分了。最简单的说明如何设置文本属性。正如我之前提到的,我试图在工作线程上尽可能多地保持处理,并且只调用相应的TreeView方法进行更新,因为这个过程可能需要很长时间(一次解析和显示数百个过程)
是否有一个很好的方法可以做到这一点,还是我应该把我的肿块和我的整个依赖树传递回我的主表单?
以下是我目前用于显示第一级依赖关系的代码。请记住,这最终将是递归的(目前处于“get it working”模式),这就是为什么我要将它保持在UI线程之外:
Public Sub updateTreeView()
Dim arrNodeList As ArrayList
Dim childNode As clsProcedureNode
Dim currentNode As clsProcedureNode
Dim intChildIndex As Integer
Dim intNodeListIndex As Integer
Dim treeView As TreeView
//Lock main form
SyncLock mMainForm
//Check that we are actually running on a seprate thread
If mMainForm.InvokeRequired() = True Then
//Call delegate to get handle to TreeView
treeView = mMainForm.Invoke(mGetTreeViewDelegate)
//Add Parsed array to main form TreeView
For intNodeListIndex = 0 To mProcedureNodes.Length - 1
//Get current node and its child list
currentNode = mProcedureNodes(intNodeListIndex)
arrNodeList = currentNode.getProcsCalled()
//Add node and all children to TreeView
With treeView
.BeginUpdate()
.Nodes.Add(currentNode.getName())
For intChildIndex = 0 To arrNodeList.Count
childNode = arrNodeList.Item(intChildIndex)
.Nodes(intNodeListIndex).Nodes.Add(childNode.getName())
Next
.EndUpdate()
End With
Next
End If
End SyncLock
End Sub
答案 0 :(得分:2)
为什么不使用BackgroundWorker?它比Invoke和同步更容易使用。事实上,它旨在缓解这样的事情。
您可以在DoWork
事件处理程序中执行后台工作,该事件处理程序在其他线程上运行,并安全地与您在UI线程上运行的ProgressChanged
事件处理程序中的UI进行交互。使用ProgressChanged
的{{1}}方法在DoWork
中表示需要调用ReportProgress
。
答案 1 :(得分:0)
嗯,安全操作UI控件的唯一地方是UI线程。这在WinForms和WPF中也是如此。你可以将数据传回UI线程并在那里填充整个树,或者你做这样的事情(伪代码):
Sub updateView()
foreach item in data
mainForm.AddToTreeView(item);
Thread.Sleep(1);
在单独的线程上执行此操作应避免重载UI线程,但可能需要很长时间才能填充。 AddToTreeView需要在UI线程上调用add。