在Visual Basic中从另一个线程锁定控件

时间:2011-03-18 22:06:17

标签: .net vb.net multithreading design-patterns

我正在开发一个解析器应用程序,用于从已从数据库中提取的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

2 个答案:

答案 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。