1

我在树视图中遇到问题,我尝试根据 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
        { }
4

3 回答 3

2

你有几个问题。

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

任何在这里监视我的答案的人都知道我对这些编组技术的感受(例如Invoke)。它可能(并且通常是)最糟糕的更新 UI 的方法。事实上,它被过度使用了,以至于我认为它可以被视为一种货物崇拜编程形式。

您应该做的是让您的工作线程将更新所需的数据发布到队列中,然后让您的 UI 线程在最适合您的时间间隔内TreeView将其拉出。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.
        }
      }
    }
  }
}
于 2012-05-25T17:24:31.353 回答
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
    { }
于 2012-05-25T14:30:49.347 回答
0

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

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

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

于 2012-05-25T13:57:11.907 回答