Treeview异步问题

时间:2012-05-25 12:38:59

标签: c# multithreading treeview

我遇到了一个树视图的问题,我试图添加一个基于Node.Text索引的子节点(我也尝试过这个基于int索引 - 无济于事)。这在同步运行时非常有效。但是,我运行完全相同的事情Async(backgroundWorker)它抛出一个未处理的ArgumentOutOfRange异常。另一个奇怪的部分是我试图在两个不同的领域中捕获这个例外。见代码:

 using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
        {
            int x = 0;
            foreach (string subkey_name in key.GetSubKeyNames())
            {
                using (RegistryKey subkey = key.OpenSubKey(subkey_name))
                {
                    foreach (string a in (string[])subkey.GetValue("Users", ""))
                    {
                        User u = new User(a);
                        usrs.addUser(new User(a));
                        wgs.addUserToWorkgroup(subkey_name, a);
                        usrs.AddWorkGroupToUser(subkey_name, a);
                        int trycount = 0;
                    TryAgain:
                        try
                        {
                            //here is where the exception occurs
                            ExecuteSecure(() => treeView1.Nodes[subkey_name].Nodes.Add(a, a));
                        }
                        catch (ArgumentOutOfRangeException)//This does not catch it.
                        {
                            trycount++;
                            if (trycount < 100)
                            {
                                goto TryAgain; //b/c I cannot catch it this never happens...
                            }
                        }

                    }
                }

                x++;
                //System.Threading.Thread.Sleep(2);
                //As you can see I've tried to let the tread sleep to resolve this 
                //- it will get a little farther but still eventually bomb out.
            }
        }

这是ExecuteSecure代码(https://stackoverflow.com/a/8021020/1387186)

 private void ExecuteSecure(Action a)
    {
        o = new object();
        try
        {
            if (InvokeRequired)
            {
                lock (o)
                {
                    BeginInvoke(a);
                }
            }
            else
                a();
        }
        catch (Exception) //again **sigh** this does not catch the error
        { }

3 个答案:

答案 0 :(得分:2)

你有几个问题。

  • 您正在关闭循环变量。
  • 您的锁定毫无意义,因为您每次都使用不同的实例。
  • 你的锁是没有意义的,因为没有其他线程可以竞争它。
  • 您使用Invoke的新解决方案会导致UI和工作线程来回打乒乓,因此您的工作线程现在变得毫无用处。

在这里监视我的答案的任何人都知道我对这些编组技术的感受(如Invoke)。它可能(通常是)更新UI的最差方法。事实上,它已经过度使用,已经达到了我认为它可以被视为cargo cult programming形式的程度。

您应该做的是让您的工作线程将更新TreeView所需的数据发布到队列中,然后让您的UI线程通过System.Windows.Forms.Timer以最适合的时间间隔将其拉出来您。这就是它的样子。

public class YourForm : Form
{
  private sealed class YourData
  {
    public string SubKeyName { get; set; }
    public string Value { get; set; }
  }

  private ConcurrentQueue<YourData> queue = new ConcurrentQueue<YourData>();

  private void StartTreeViewUpdate_Click(object sender, EventArgs args)
  {
    Task.Factory.StartNew(WorkerThread);
    TreeViewUpdateTimer.Enabled = true;
  }

  private void TreeViewUpdateTimer_Tick(object sender, EventArgs args)
  {
    // Update in batches of 100 (or whatever) so that the UI stays
    // responsive.
    treeView1.BeginUpdate();
    for (int i = 0; i < 100; i++)
    {
      YourData value = null;
      if (queue.TryDequeue(value) && value != null)
      {
        treeView1.Nodes[value.SubKeyName].Nodes.Add(value.Value, value.Value)
      }
      else
      {
        // We're done.
        TreeViewUpdateTimer.Enabled = false;
        break;
      }
    }
    treeView1.EndUpdate();
  }

  private void WorkerThread()
  {
    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key))
    {
      foreach (string subkey_name in key.GetSubKeyNames())
      {
        using (RegistryKey subkey = key.OpenSubKey(subkey_name))
        {
          foreach (string a in (string[])subkey.GetValue("Users", ""))
          {
            User u = new User(a);
            usrs.addUser(new User(a));
            wgs.addUserToWorkgroup(subkey_name, a);
            usrs.AddWorkGroupToUser(subkey_name, a);
            var data = new YourData();
            data.SubKeyName = subkey_name;
            data.Value = a;
            queue.Enqueue(data);
          }
          queue.Enqueue(null); // Indicate that queueing is done.
        }
      }
    }
  }
}

答案 1 :(得分:0)

如果评论很明显,请执行以下操作:

 var uncapturedSubKey_name = subkey_name;
 ExecuteSecure(() => treeView1.Nodes[uncapturedSubKey_name].Nodes.Add(a, a));

lambda正在捕获另一个变量,并且在循环完成之后才会执行(即最后一个subkey_name ...

答案 2 :(得分:0)

我能够解决这个问题:

通过将BeginInvoke()更改为Invoke()   我也改变了O的范围,所以它确实锁定了。

private void ExecuteSecure(Action a)
{

    try
    {
        if (InvokeRequired)
        {
            lock (o)
            {
                Invoke(a);
            }
        }
        else
            a();
    }
    catch (Exception) //again **sigh** this does not catch the error
    { }