我对线程很新,所以我的想法和问题可能有点愚蠢:)
我使用来自另一个线程的数据填充WinForm
控件,因此当我尝试访问控件时,我必须调用Invoke()
。
如果我理解正确,treeView.BeginInvoke(/*some Action()*/)
会使Action<>()
在主线程中运行。但我“解雇并忘记”这个BeginInvoke()
,所以我不知道这项工作何时完成。即使工作线程关闭并且执行返回主线程,我也无法确定所有BeginInvoke()
方法是否已完成执行。
这就是为什么即使在返回主线程后我也无法使用Control
来解决BeginInvoke()
。{/ p>
实际问题 TreeView.ExpandAll()
不起作用。
请看下面的代码段。
private void btnGetTree_Click(object sender, EventArgs e) {
var treeViewWriter = new Thread(() => UpdateTreeView(new AddXmlNodeArgs(di, null), treeDirectoryContents));
treeViewWriter.Start();
treeViewWriter.Join();
treeDirectoryContents.ExpandAll();
}
// method runs on a worker thread
public static void UpdateTreeView(AddXmlNodeArgs args, TreeView treeView) {
// I will miss details... Here is the code that I run for every new TreeNode:
treeView.UpdateTree((TreeView tree) => {
tree.Nodes[0].Nodes.Add(newTreeNode); // treeView.Nodes[0]...
});
}
// Extension method for TreeView
public static void UpdateTree(this TreeView tree, Action<TreeView> code) {
if (tree.InvokeRequired)
tree.BeginInvoke(code, tree);
else
code.Invoke(tree);
}
我解雇了tree.BeginInvoke()
,但我没有在任何地方拨打EndInvoke()
。所以我想当btnGetTree_Click
执行到达treeDirectoryContents.ExpandAll()
时 - 并非所有Invoke()
方法都完成了他们的工作。这就是为什么.ExpandAll() doesn't work
。
如果我错了,请纠正我,请就如何解决这个问题提出建议。
答案 0 :(得分:1)
这是绝对错误的:
treeViewWriter.Start();
treeViewWriter.Join();
永远不要从主线程调用Thread.Join!因为Join
冻结了应用程序,所有BeginInvoke
/ Invoke
永远不会完全执行因为消息没有处理。
BeginInvoke()
实际上是这样的:
Application.DoEvents()
中弹出此消息(或类似内容,总是在Application.Run()
中调用)BeginInvoke()
WaitHandle
中的IAsyncResult
)EndInvoke()
等待此类信号(或者如果IAsyncResult
中的BeginInvoke
从未存储,则会被垃圾收集)所以再次:你只是把它写成事件驱动的或者做这样的事情:
private bool done = false;
void click(object, EventArgs) {
thread.Start();
while(!done) Application.DoEvents();
tree.ExpandAll();
}
<强> ADDON:强>
Eihter使用Invoke()
(已同步)和上面的循环Application.DoEvents()
或者使用BeginInvoke()并以相同的方式调用ExpandAll(通过线程中的BeginInvoke())
<强> ADDON2:强>
private bool done;
void click(object,EventArgs) {
done = false; // init state
new Thread(work).Start(); // start backgound work
while(!done) Application.DoEvents(); // wait until done
finish(); } // finish the job in main thread
void work() {
Thread.Sleep(100); // do your work
done = true; } // signal done
void finish() {
whatever(); } // called on main thread
void click2(object,EventArgs) {
new Thread(work2).Start(); } // just start the hread
void work2() {
Thread.Sleep(100); // do your work
BeginInvoke(new Action(finish)); } // execute finish() on main thread
答案 1 :(得分:0)
创建一个Action
Invoke
代表,然后BeginInvoke
该代理。
这样您就可以使用回调函数将ExpandAll
移动到:
if (tree.InvokeRequired)
new Action(() => { tree.Invoke(code, tree); }).BeginInvoke((ar) => {
treeDirectoryContents.ExpandAll();
}, null);
else
code.Invoke(tree);
请注意,我使用简单的BeginInvoke
替换了您的原始Invoke
。
更新:由于firda正确提到,因为主线程在Join
方法内被阻塞,等待另一个线程退出,所以在控件上执行Invoke
将导致死锁。现在您的ExpandAll
已移至回调,您应该删除Join
,一切都会好的。