本文介绍了线程化或克隆复杂对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我在使用C#.NET编程时遇到了一个非常奇怪的问题: 有一个WebBrowser控件,可以在我的程序和按钮中加载一些网站。 一开始,在按钮点击事件中我以这种方式启动线程: 线程oThread = new 线程( new ThreadStart(StartFunction)); oThread.Start(); 现在,在StartFunction中我正在这样做: // 这是对话的开始我们正在等待 public void StartFunction() { // 以下是我们正在等待的对话的开始 bool Connected = false ; while (!已连接) { HtmlElementCollection elc = null ; this .Invoke((MethodInvoker) delegate { elc = webBrowser.Document.Body.All; }); foreach (HtmlElement element in elc) { if (element == null || element.InnerHtml == null ) { 继续; } // 确定它是什么? if (element.InnerHtml.StartsWith( connected) )) { Connected = true ; } } } } 异常ArgumentOutOfIndex抛出foreach循环,因为元素在循环内部变化。 解决方案: 似乎复制这些项目的最佳方法是通过在WebBrowser的线程上运行它们来冻结它们(因此它们无法更改)。 List< HtmlElement> collection = null ; 行动CopyAction =()= > { collection = new List< HtmlElement>(webBrowser.Document.All.Count); foreach (HtmlElement element in webBrowser.Document.All) { collection.Add(element); } }; webBrowser.Invoke(CopyAction); 现在你可以操纵'收集'而不必担心元素被修改。解决方案 这是一个链接,帮助解释谢尔盖试图让你了解关闭捕获变量(不是)的内容价值!) C#5.0中捕获的闭包(循环变量) - 堆栈溢出 [ ^ ] 有趣的是,由于C#5.0切换了 for ( int i = 0 ; i < elc.Count - 1 ; ++ i) { HtmlEl ement element = null ; // 编辑:澄清调用的委托作业已删除,代码如下。 this .Invoke((MethodInvoker) delegate { element = elc [我]; }); foreach (HtmlElement元素 in elc) { 解决捕获的变量和索引错误! (这只是解决方案几乎没有资格,但是没有好的方法在评论中标记代码。) ===== ====== 编辑2016年4月11日 - MTH: 由于收藏品在不合时宜的时候从你的下方改变了,你需要得到以更安全的方式收集该集合。 (除了谢尔盖的评论之外,这一切都表明你需要重新考虑这个策略!) 在UI线程中运行检查可以防止在检查通过集合过程中更改集合。 // 这是w的对话的开始正在等待 public void StartFunction() { // 以下是我们正在等待的对话的开始 bool 已连接= false ; 行动connectedCheck =()= > {已连接= webBrowser1.Document.Body.All.Cast< HtmlElement>()。任何( el = > { bool conn = false ; if (el!= null ) { string inner = el.InnerHtml; if (inner != null ) conn = inner.StartsWith( connected); } return conn; }); }; while ( true ) { // connectedCheck应该非常快。 / / 没有理由不能在UI线程中运行。 / / (这应该可以防止集合在运行时被修改。) webBrowser1.Invoke(connectedCheck); if (已连接) return ; // 现在等待指示网页上发生了更改的内容。 // 不要只是运行此检查open loop!! } } 这是一个想法:总是需要调用,因为你做的一切都不是在UI线程中。你甚至不需要检查 InvokeRequired - 它总是会返回true。 使用浏览器实例,即使您使用非UI成员,这也是同一个故事。您在一个线程中创建它,并在另一个线程中使用其索引属性(通过。[index] )。如果你在UI线程中创建它,也可以将调用委托给UI线程。 但要小心。太多的同步可能表明整个设计是错误的。您可以将这么多委托给UI线程,您可能会使情况变得比您根本不使用 oThread 更糟糕。根据你的信息,我不太确定;这只是一些值得思考的食物。如果是这种情况,我可能会建议更好的设计,只要我有更多关于你想要达到的信息。 // 这个错误: for ( int i = 0 ; i < elc.Count; i ++) // 应为: for ( int i = 0 ; i < elc.Count -1; ++ i) 正确使用调试器。 -SA I came across very strange problem when programming in C# .NET:There is a WebBrowser control, which loads some websites, in my program and a button.At the very beginning, in button click event I'm starting thread this way:Thread oThread = new Thread(new ThreadStart(StartFunction));oThread.Start();Now, inside StartFunction I'm doing this:// Here is the beginning of conversation for which we are waiting public void StartFunction() { // Here is the beginning of conversation for which we are waiting bool Connected = false; while (!Connected) { HtmlElementCollection elc = null; this.Invoke((MethodInvoker)delegate { elc = webBrowser.Document.Body.All; }); foreach (HtmlElement element in elc) { if (element == null || element.InnerHtml == null) { continue; } // determine what is it? if (element.InnerHtml.StartsWith("connected")) { Connected = true; } } } }The exception ArgumentOutOfIndex is thrown on foreach loop, because the elements get changed while inside the loop.Solution:It seems that the best way to copy those items is to freeze them by running them on thread of WebBrowser (so they can't get changed).List<HtmlElement> collection = null;Action CopyAction = () =>{ collection = new List<HtmlElement>(webBrowser.Document.All.Count); foreach (HtmlElement element in webBrowser.Document.All) { collection.Add(element); }};webBrowser.Invoke(CopyAction);Now you can just manipulate on 'collection' without worrying that the elements get modified. 解决方案 Here is a link to help explain what Sergey is trying to have you understand about what is going on with the closure capturing the variable (not the value!)Captured Closure (Loop Variable) in C# 5.0 - Stack Overflow[^]It's interesting to note that, since C# 5.0 switching the for (int i = 0; i < elc.Count - 1; ++i){ HtmlElement element = null; // EDIT: clarify that Invoked delegate assignment is removed with code below. this.Invoke((MethodInvoker)delegate { element = elc[i]; });to beforeach (HtmlElement element in elc){solves both the captured variable and indexing errors!(This only barely qualifies as a "Solution", but there's no good way to markup code in a comment.)===========Edit Apr 11, 2016 -- MTH:Since the collection is getting changed out from under you at inopportune times, you need to get a copy of the collection in a safer way.(This is all in addition to Sergey's comments suggesting that you need to rethink the strategy in general!)Running the check in the UI thread should prevent the collection from being changed while a checking "pass through the collection" is in progress.// Here is the beginning of conversation for which we are waitingpublic void StartFunction(){ // Here is the beginning of conversation for which we are waiting bool Connected = false; Action connectedCheck = () => { Connected = webBrowser1.Document.Body.All.Cast<HtmlElement>().Any(el => { bool conn = false; if (el != null) { string inner = el.InnerHtml; if (inner != null) conn = inner.StartsWith("connected"); } return conn; }); }; while (true) { // connectedCheck should be quite fast. // There should be no reason that it can't run in the UI thread. // (Which should prevent the collection from being modified while it is running.) webBrowser1.Invoke(connectedCheck); if (Connected) return; // Now wait for something that indicates a change has happened on the web page. // Don't just run this check "open loop"!! }}Here is the idea: invoke is always required, because you do everything not in UI thread. You don't even need to check InvokeRequired — it will always return true.With the browser instance, this is the same story, even if you use non-UI members. You created it in one thread, and use, for example, its indexed property (by taking .[index]) in another thread. If you create it in UI thread, also delegate the call to UI thread.Be careful though. Too much synchronization may indicate that the whole design is wrong. It's possible that you can delegate so much to UI thread, you may make the situation worse than you could have not using that oThread at all. I'm not so sure, based on your information; this is just some food for thought for you. If this is the case, I would be probably able to suggest better design only if I had a lot more information on what you want to achieve.[EDIT]// This is wrong:for (int i = 0; i < elc.Count; i++)// should be:for (int i = 0; i < elc.Count -1; ++i)Use the debugger properly.—SA 这篇关于线程化或克隆复杂对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-29 01:09