Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Treeview异步问题_C#_Multithreading_Treeview - Fatal编程技术网

C# Treeview异步问题

C# Treeview异步问题,c#,multithreading,treeview,C#,Multithreading,Treeview,我在treeview中遇到了一个问题,我试图根据Node.Text索引添加一个子节点(我也尝试了基于int索引的方法,但没有效果)。当同步运行时,它的工作非常出色。但是,我运行的是与Async(backgroundWorker)完全相同的东西,它抛出了一个未处理的ArgumentOutOfRange异常。另一个奇怪的部分是,我试图在两个不同的领域捕捉这个异常。见代码: using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.

我在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.
            }
        }

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

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

lambda正在捕获另一个变量,直到循环完成后才执行(即最后一个子键名称…

我能够这样解决这个问题:

通过将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
    { }

你有几个问题

  • 您正在关闭循环变量
  • 你的锁是没有意义的,因为你每次都使用不同的实例
  • 你的锁是没有意义的,因为没有其他线程竞争它
  • 您使用
    Invoke
    的新解决方案会导致UI和工作线程来回乒乓,因此您的工作线程现在变得无用
在这里监视我答案的任何人都知道我对这些封送技术(如
调用
)的感受。它可能是(通常是)更新UI的最糟糕的方法。事实上,它被过度使用,以至于我认为它可以被视为一种形式的封送

您应该做的是让您的工作线程将更新
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.
        }
      }
    }
  }
}
公共类YourForm:Form
{
私有密封类数据
{
公共字符串子关键字名{get;set;}
公共字符串值{get;set;}
}
私有ConcurrentQueue=新ConcurrentQueue();
私有void StartTreeViewUpdate_单击(对象发送者、事件args args)
{
Task.Factory.StartNew(WorkerThread);
TreeViewUpdateTimer.Enabled=true;
}
私有void树eviewUpdateTimer_Tick(对象发送方、事件args args)
{
//成批更新100次(或其他),以便UI保持不变
//反应迅速。
treeView1.BeginUpdate();
对于(int i=0;i<100;i++)
{
YourData值=null;
if(queue.TryDequeue(value)&&value!=null)
{
treeView1.Nodes[value.SubKeyName].Nodes.Add(value.value,value.value)
}
其他的
{
//我们结束了。
TreeViewUpdateTimer.Enabled=false;
打破
}
}
treeView1.EndUpdate();
}
私有void WorkerThread()
{
使用(RegistryKey key=Registry.LocalMachine.OpenSubKey(注册表项))
{
foreach(key.GetSubKeyNames()中的字符串子键\名称)
{
使用(RegistryKey subkey=key.OpenSubKey(subkey\u name))
{
foreach(在(string[])子键.GetValue(“用户”,“用户”)中的字符串a)
{
用户u=新用户(a);
usrs.addUser(新用户(a));
wgs.addUserToWorkgroup(子项名称,a);
usrs.AddWorkGroupToUser(子项名称,a);
var data=newyourdata();
data.SubKeyName=子键名称;
数据值=a;
排队(数据);
}
queue.Enqueue(null);//指示排队已完成。
}
}
}
}
}

关闭for循环变量的另一个牺牲品。通过锁定(o)可能与您尝试实现的目标重复?这似乎是为了阻止并发操作执行。它不起作用,因为您使用了本地对象来锁定。在
BeginInvoke
之前的锁定(o)是无意义的,o是一个新值(也就是说,其他任何东西都不能用它来锁定)然后,
BeginInvoke
会立即返回…这就是为什么你什么都看不到的原因。我不相信我真的要结束这里的变量,根据我在这里读到的内容,我这么说:尽管如此,你的想法似乎是最好的选择。我一直在调试自己,想找到一个可以重复使用和学习的消息传递系统,但我只有耳鼻喉科优先考虑。也许你会是我的灵感源泉,以正确的方式做事。你能提供一些好的出发点吗?
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.
        }
      }
    }
  }
}