【问题标题】:Locking a Control from Another Thread in Visual Basic在 Visual Basic 中从另一个线程锁定控件
【发布时间】:2011-03-18 22:06:16
【问题描述】:

我正在开发一个解析器应用程序,以根据从数据库中提取的 DDL 文件构建调用树。我们的想法是获取大量这些 DDL 文件并准确确定什么叫什么。为此,我使用了 .NET TreeView。我正在努力的最终输出是这样的:

-Proc1
  -Proc2
    -Proc3
  -Proc4
-Proc2
  -Proc3
-Proc3
-Proc4

现在,我的所有解析都正常工作。虽然,这个过程相当漫长。因此,我决定将所有重量级处理转移到它自己的线程上。一切都按预期工作,直到我需要更新 TreeView。我试图将所有实际更新逻辑保留在单独的线程上,并且只更新 TreeView。然而,尽管我的主窗体 SyncLocked,当我尝试访问树时仍然遇到异常。

我在网上找到了很多示例,这些示例展示了如何使用 Delegates 进行线程安全访问,但不幸的是,它们对于我的需求来说都有些简单。大多数只是显示如何设置文本属性。正如我之前提到的,我试图在工作线程上保留尽可能多的处理,并且只调用相应的 TreeView 方法来更新,因为这个过程可能非常冗长(一次解析和显示数百个过程) .

有什么好的方法可以做到这一点,还是我应该只管把我的整个依赖树传递回我的主窗体?

这是我目前用来显示第一级依赖关系的代码。请记住,这最终将是递归的(目前处于“让它工作”模式),这就是为什么我想让它远离 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

【问题讨论】:

  • 您不必在 UI 线程上操作树视图吗?您将树视图拉出主窗体,然后将其填充到线程上。我认为您需要将数据传递回 UI 线程,然后在那里填充。
  • 这实际上是我想要避免的。该过程分两个步骤进行。解析 DDL 文件,然后构建树。由于我正在使用的数据集非常大且具有递归性质,我正在尝试在线程外构建树,以便在填充树时 UI 不会挂起。

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


【解决方案1】:

为什么不使用BackgroundWorker?它比调用和同步更容易使用。事实上,它的设计目的是为了缓解这种情况。

您可以在运行在其他线程上的DoWork 事件处理程序中进行后台工作,并在运行在 UI 线程上的ProgressChanged 事件处理程序中与您的 UI 安全地交互。使用BackgroundWorkerReportProgress 方法在DoWork 中表明需要调用ProgressChanged

【讨论】:

  • 我见过BackgroundWorker,但对我来说操作原始线程似乎更自然。如果我想使用这种方法,我假设我必须在 UI 线程上构建一个函数来抽象将节点插入树的过程,然后安全地调用它? reportProgress() 在我看来有点误导,因为该过程实际上需要几个步骤,每个步骤都会修改 UI 的不同部分。这也是我选择我所做的设计的部分原因,因为我希望它也可以扩展以处理 Treeview。
  • 对不起,我不明白。 BackgroundWorker 完全符合您的要求 - 它只是一个包装器,为您提供一个“原始”线程来执行您的后台工作,并提供一个单独的回调来执行您的 UI 线程。这两者必须分开,你不能在工作线程中弄乱 UI。它甚至不介意它是否有多个步骤,您可以为每个步骤使用多个 Worker。在这种情况下,自己做很容易出错且没有必要。
  • 如果您不想使用后台工作人员,除了答案之外,您还可以尝试其中之一 2 尝试查看多线程示例中的stackoverflow.com/questions/17266102/… 答案和来自 ( me ) for In Model Threading.... 那些 2 应该给你编写的代码来开始。
【解决方案2】:

嗯,安全操作 UI 控件的唯一地方是 UI 线程。在 WinForms 和 WPF 中都是如此。您可以将数据传回 UI 线程并在那里填充整个树,也可以执行以下操作(伪代码):

Sub updateView()
   foreach item in data
      mainForm.AddToTreeView(item);
      Thread.Sleep(1);

在单独的线程上执行此操作应避免 UI 线程过载,但填充可能需要很长时间。 AddToTreeView 需要在 UI 线程上调用 add。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 2017-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多