如何确保异步方法完成工作?

我对线程很新,所以我的想法和问题可能有点傻:)

我用另一个线程的数据填充WinForm控件,所以当我试图访问控件时我必须调用Invoke()

如果我理解正确, treeView.BeginInvoke(/*some Action()*/)使这个Action()在主线程中运行。 但我“开火并忘记”这个BeginInvoke() ,所以我不知道什么时候工作真的完成了。 即使工作线程关闭并且执行返回主线程,我也无法确定所有BeginInvoke()方法是否已完成执行。

这就是为什么即使在返回主线程之后我也无法使用Control来管理我触发的BeginInvoke()

实际问题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 code) { if (tree.InvokeRequired) tree.BeginInvoke(code, tree); else code.Invoke(tree); } 

我解雇了tree.BeginInvoke()但我没有在任何地方调用EndInvoke() 。 所以我想在btnGetTree_Click执行时到达treeDirectoryContents.ExpandAll() – 并非所有的Invoke()方法都完成了他们的工作。 这就是为什么.ExpandAll() doesn't work

如果我错了,请纠正我,并请提出如何解决这个问题的建议。

这是绝对错误的:

 treeViewWriter.Start(); treeViewWriter.Join(); 

永远不要从主线程调用Thread.Join! 因为Join冻结了应用程序,并且所有那些BeginInvoke / Invoke永远不会完全执行,因为消息未被处理。

这就是BeginInvoke()实际工作的方式:

  1. 它在消息循环上发送一些WM_USER(或类似的东西)。
  2. 主线程在Application.DoEvents() (或类似的,在Application.Run() )中弹出此消息)
  3. 主线程执行传递给BeginInvoke()的委托
  4. 主线程信号执行结束(通过IAsyncResultWaitHandle
  5. EndInvoke()等待这样的信号(或者如果从不存储来自BeginInvoke IAsyncResult ,它将被垃圾收集)

所以再说一遍:你只是把它写成事件驱动的或者做这样的事情:

 private bool done = false; void click(object, EventArgs) { thread.Start(); while(!done) Application.DoEvents(); tree.ExpandAll(); } 

ADDON Eihter使用Invoke() (synchronized)和上面的循环与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 

创建一个Invoke委托的Action ,然后BeginInvoke该操作。 这样你就可以有一个回调,你可以将ExpandAll移动到:

 if (tree.InvokeRequired) new Action(() => { tree.Invoke(code, tree); }).BeginInvoke((ar) => { treeDirectoryContents.ExpandAll(); }, null); else code.Invoke(tree); 

请注意,我用简单的Invoke替换了原始的BeginInvoke

更新:由于firda正确提到,因为主线程在Join方法中被阻塞,等待另一个线程退出,所以在控件上执行Invoke将导致死锁。 现在您的ExpandAll已移至回调,您应该删除Join ,一切都会好的。