C# Treeview异步问题
我在treeview中遇到了一个问题,我试图根据Node.Text索引添加一个子节点(我也尝试了基于int索引的方法,但没有效果)。当同步运行时,它的工作非常出色。但是,我运行的是与Async(backgroundWorker)完全相同的东西,它抛出了一个未处理的ArgumentOutOfRange异常。另一个奇怪的部分是,我试图在两个不同的领域捕捉这个异常。见代码:C# Treeview异步问题,c#,multithreading,treeview,C#,Multithreading,Treeview,我在treeview中遇到了一个问题,我试图根据Node.Text索引添加一个子节点(我也尝试了基于int索引的方法,但没有效果)。当同步运行时,它的工作非常出色。但是,我运行的是与Async(backgroundWorker)完全相同的东西,它抛出了一个未处理的ArgumentOutOfRange异常。另一个奇怪的部分是,我试图在两个不同的领域捕捉这个异常。见代码: using (Microsoft.Win32.RegistryKey key = Registry.LocalMachine.
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
{ }
你有几个问题
- 您正在关闭循环变量
- 你的锁是没有意义的,因为你每次都使用不同的实例
- 你的锁是没有意义的,因为没有其他线程竞争它
- 您使用
的新解决方案会导致UI和工作线程来回乒乓,因此您的工作线程现在变得无用Invoke
调用
)的感受。它可能是(通常是)更新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.
}
}
}
}
}