问题描述
我做的东西其实就是类似多标签浏览器那样,每个子窗体都有个axWebbrowser控件,子窗体打开后就自动填表提交,如果不用多线程打开窗体,当同时打开多个子窗体的时候,主窗体的操作就会卡住。我现在用下面的代码可以用多线程打开个子窗体,但是这个子窗体不是主窗体的MDI子窗体,这不是我想要的。我就是在新线程的委托里写frmMdiChild.MdiParent=this;报错,说“线程间操作无效:从不是创建控件""的线程访问它。”。麻烦各位大侠帮忙看看,如果分不够的话我可以再加100分usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Windows.Forms;usingSystem.Threading;usingSystem.Windows.Threading;//需引入WindowsBase组件namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}privatevoidtoolStripButton1_Click(objectsender,EventArgse){MessageBox.Show("UI线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());ThreadnewWindowThread=newThread(newThreadStart(ThreadStartingPoint));newWindowThread.SetApartmentState(ApartmentState.STA);newWindowThread.IsBackground=true;newWindowThread.Start();}///<summary>///创建子窗体的新线程///</summary>privatevoidThreadStartingPoint(){FormfrmMdiChild=newForm();frmMdiChild.Load+=newSystem.EventHandler(this.frmMdiChild_Load);//form1.MdiParent=this;MyInvokemi=newMyInvoke(setMdi);BeginInvoke(mi,(object)frmMdiChild);frmMdiChild.Show();System.Windows.Threading.Dispatcher.Run();//如果没这个子窗体打开后就马上关掉}publicdelegatevoidMyInvoke(objectobj);privatevoidsetMdi(objectform){this.textBox1.Text="我是从多线程中写入的";//可以在线程中写入主窗体的textbox1FormfrmMdiChild=(Form)form;frmMdiChild.MdiParent=this;//这里报错,去掉这个就可以了,但新窗体就不是mdi子窗体了//frmMdiChild.Owner=this;}///<summary>///子窗体的Load事件///</summary>privatevoidfrmMdiChild_Load(objectsender,EventArgse){MessageBox.Show("子窗体线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());}}}
解决方案
解决方案二:
解决方案三:
没人吗?自己顶
解决方案四:
自己顶
解决方案五:
线程之间是不能直接操作的。
解决方案六:
引用4楼huanggreat的回复:
线程之间是不能直接操作的。
所以我用了委托啊
解决方案七:
引用4楼huanggreat的回复:
线程之间是不能直接操作的。
所以我用了委托啊
解决方案八:
引用6楼seeyou8888的回复:
Quote: 引用4楼huanggreat的回复:
线程之间是不能直接操作的。所以我用了委托啊
FormfrmMdiChild=newForm();也得放在委托里,你把这个放在线程里创建,相当于是线程创建的窗体,你要在UI线程里访问,一样的跨线程了
解决方案九:
因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点
解决方案十:
引用7楼xiaozhu39505的回复:
Quote: 引用6楼seeyou8888的回复:
Quote: 引用4楼huanggreat的回复:
线程之间是不能直接操作的。所以我用了委托啊
FormfrmMdiChild=newForm();也得放在委托里,你把这个放在线程里创建,相当于是线程创建的窗体,你要在UI线程里访问,一样的跨线程了
这个我试过了,这样做就相当于子窗体还是在主线程上,axWebbrowser一打开网页主窗体又会被卡住了
解决方案十一:
引用8楼KarasCanvas的回复:
因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点
那重点是什么啊,我那种效果实现不了吗?
解决方案十二:
把多线程创建UI的操作串行化到主线程中去做,多线程只做一些创建UI之前耗时的数据准备操作
解决方案十三:
解决方案十四:
引用10楼seeyou8888的回复:
Quote: 引用8楼KarasCanvas的回复:
因为网上那些杂七杂八的教程都说了这么一个概念,多线程操作控件要用委托,其实根本不是重点那重点是什么啊,我那种效果实现不了吗?
UI线程是单线程,你任何操作UI的界面的操作都需要在UI线程里执行,所以如果你UI线程里面操作太多,UI不卡不可能的,你必须把费时的跟UI无关的操作放到其他线程里执行,尽量减少在UI线程跑的代码
解决方案十五:
引用11楼wayu002的回复:
把多线程创建UI的操作串行化到主线程中去做,多线程只做一些创建UI之前耗时的数据准备操作
这个不懂,其实也没什么其他的操作,就是单单是让axWebbrowser打开一个网页,就这么简单的操作都会卡住几秒钟.这个是axWebbrowser的问题,查了网上的说要把axWebbrowser放到不同的进程上去,这样不知道能不能做成mdi子窗体的形式了
解决方案:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}
与一切与主窗体有关的操作都需要跨线程,这个跨线程是伪的,实际上是在主线程上执行,如果没有特殊需要,多线程还是少用,毕竟由调度产生的上下文切换带来的性能损失,以及由线程创建和销毁带来的内存消耗并不低。
解决方案:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}
注册了个Load事件处理,目测在子线程中执行,测试通过
解决方案:
Navigate方法是异步的,不应该卡界面才对,你给的例子里面也没用到相关控件,不知道你是如何被卡住的。
解决方案:
引用16楼Coffee_MX的回复:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}注册了个Load事件处理,目测在子线程中执行,测试通过
你这个子窗体的线程还是主线程啊MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());你把这个代码放到主窗体和子窗体的Load事件看看,我在子窗体上放了个WebBrowser,然后打开网页,主窗体就会被卡住了//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());WebBrowserweb1=newWebBrowser();f.Controls.Add(web1);web1.Navigate("http://www.pconline.com.cn");};
解决方案:
访问Windows窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NETFramework有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个InvalidOperationException,并提示消息:“从不是创建控件controlname的线程访问它。”此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以.NETFramework2.0版之前的.NETFramework编写的应用程序时,可能会出现此异常。注意可以通过将CheckForIllegalCrossThreadCalls属性的值设置为false来禁用此异常。这会使控件以与在VisualStudio2003下相同的方式运行。CheckForIllegalCrossThreadCalls=false;
解决方案:
引用18楼seeyou8888的回复:
Quote: 引用16楼Coffee_MX的回复:
usingSystem.Threading;usingSystem.Windows.Forms;namespaceWindowsFormsApplication1{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();this.IsMdiContainer=true;}privatevoidaaaToolStripMenuItem_Click(objectsender,System.EventArgse){ThreadthNewForm=newThread(()=>{Formf=newForm();//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.MdiParent=this));}#ifDEBUGSystem.Console.WriteLine("created");#endif//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;};//跨线程if(this.InvokeRequired){this.BeginInvoke((MethodInvoker)(()=>f.Show()));}f.Text="IntheChildThread";//此句在子线程中执行});thNewForm.Start();}}}注册了个Load事件处理,目测在子线程中执行,测试通过
你这个子窗体的线程还是主线程啊MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());你把这个代码放到主窗体和子窗体的Load事件看看,我在子窗体上放了个WebBrowser,然后打开网页,主窗体就会被卡住了//新加的,测试通过f.Load+=(sender1,e1)=>{f.Text="aaa";f.Width=150;f.Height=200;MessageBox.Show("线程ID:"+Thread.CurrentThread.ManagedThreadId.ToString());WebBrowserweb1=newWebBrowser();f.Controls.Add(web1);web1.Navigate("http://www.pconline.com.cn");};
UI线程最好是单线程,否则就如楼上所说,线程安全就不好了,根据你的问题,最好是直接用WebClient提交表单,或者HttpWebRequest发送请求什么的,这些都不涉及UI,所以异步调用应该是不成问题,没必要非要用Browser,涉及UI的东西,用多线程反倒会成为负担
解决方案:
延后执行Navigate方法应该不会有问题,因为Navigate方法本身是异步的,也许是你直接在初始化窗口中执行才出现的问题,未测试纯属猜测。
解决方案:
引用20楼Coffee_MX的回复:
UI线程最好是单线程,否则就如楼上所说,线程安全就不好了,根据你的问题,最好是直接用WebClient提交表单,或者HttpWebRequest发送请求什么的,这些都不涉及UI,所以异步调用应该是不成问题,没必要非要用Browser,涉及UI的东西,用多线程反倒会成为负担
没办法,一定要用Browser,因为还要人去操作
解决方案:
引用21楼qldsrx的回复:
延后执行Navigate方法应该不会有问题,因为Navigate方法本身是异步的,也许是你直接在初始化窗口中执行才出现的问题,未测试纯属猜测。
你自己试一下就知道可,就打开个http://www.pconline.com.cn然后后快速移动窗体,你就发现会卡住那两,三秒,电脑好的话可能1秒
解决方案:
正常情况下,IE也是多线程的,但是IE里面访问也有停顿,因为界面的产生,HTML元素的布局,图片的显示等都非常消耗资源,界面里面东西越多,停顿越大,这个停顿就好比UI刷新,如果不是数据获取时的停顿,那么你就无法优化,那个显示部分是IE自己慢造成的。
解决方案:
引用24楼qldsrx的回复:
正常情况下,IE也是多线程的,但是IE里面访问也有停顿,因为界面的产生,HTML元素的布局,图片的显示等都非常消耗资源,界面里面东西越多,停顿越大,这个停顿就好比UI刷新,如果不是数据获取时的停顿,那么你就无法优化,那个显示部分是IE自己慢造成的。
我现在都想换其他的浏览器控件了