跨线程操作无效:从创建该线程的线程以外的线程访问控件
我有一个场景。 (Windows窗体,C#、. NET)
- 有一个主窗体可以承载一些用户控件。
- 用户控件执行一些繁重的数据操作,因此,如果我直接调用
UserControl_Load
方法,则UI在加载方法执行期间将无响应。 - 为了克服这个问题,我将数据加载到不同的线程上(尝试尽我所能更改现有代码)
- 我使用了一个后台工作线程来加载数据,完成后将通知应用程序它已经完成了工作。
- 现在出现了一个真正的问题。 所有UI(主窗体及其子用户控件)均在主主线程上创建。 在usercontrol的LOAD方法中,我基于userControl上某些控件(如文本框)的值获取数据。
伪代码如下所示:
代码1
UserContrl1_LoadDataMethod()
{if (textbox1.text == "MyName") // This gives exception{//Load data corresponding to "MyName".//Populate a globale variable List<string> which will be binded to grid at some later stage.}
}
它给的例外是
跨线程操作无效:从创建该线程的线程以外的线程访问控件。
要了解更多信息,我进行了一些谷歌搜索,并提出了一条建议,例如使用以下代码
代码2
UserContrl1_LoadDataMethod()
{if (InvokeRequired) // Line #1{this.Invoke(new MethodInvoker(UserContrl1_LoadDataMethod));return;}if (textbox1.text == "MyName") // Now it wont give an exception{//Load data correspondin to "MyName"//Populate a globale variable List<string> which will be binded to grid at some later stage}
}
但是但是...看来我又回到了正题。 该应用程序再次变得无响应。 如果有条件,这似乎是由于执行了第1行。 加载任务再次由父线程完成,而不是我产生的第三个任务。
我不知道我是对是错。 我是线程新手。
我该如何解决这个问题,如果执行第1行的代码阻塞还会产生什么影响?
情况是这样的 :我想根据控件的值将数据加载到全局变量中。 我不想从子线程更改控件的值。 我永远不会从子线程执行此操作。
因此,仅访问该值,以便可以从数据库中获取相应的数据。
#1楼
如果您正在使用的对象没有,这是另一种方法
(InvokeRequired)
如果您使用的是主窗体中的对象,但没有InvokeRequired,则该主窗体在除主窗体以外的类中使用主窗体时很有用
delegate void updateMainFormObject(FormObjectType objectWithoutInvoke, string text);private void updateFormObjectType(FormObjectType objectWithoutInvoke, string text)
{MainForm.Invoke(new updateMainFormObject(UpdateObject), objectWithoutInvoke, text);
}public void UpdateObject(ToolStripStatusLabel objectWithoutInvoke, string text)
{objectWithoutInvoke.Text = text;
}
它的工作原理与上面相同,但是如果您没有带有invokerequired的对象,但是可以访问MainForm,则它是不同的方法
#2楼
.NET中的控件通常不是线程安全的。 这意味着您不应从线程所在的线程之外的其他线程访问控件。 为了解决这个问题,您需要调用控件,这是您的第二个示例正在尝试的操作。
但是,您要做的就是将长时间运行的方法传递回主线程。 当然,这并不是您真正想做的。 您需要重新考虑一下,以便您在主线程上所做的所有事情都是在此处和此处设置快速属性。
#3楼
您只想使用Invoke或BeginInvoke来完成更改UI所需的最少工作。 您的“重载”方法应在另一个线程上执行(例如通过BackgroundWorker),但随后仅使用Control.Invoke / Control.BeginInvoke来更新UI。 这样,您的UI线程将可以自由处理UI事件等。
请参阅我的线程文章中的WinForms示例 -尽管该文章是在BackgroundWorker出现之前编写的,但恐怕我在这方面还没有进行更新。 BackgroundWorker只是稍微简化了回调。
#4楼
根据Prerak K的更新评论 (已删除):
我想我没有正确提出问题。
情况是这样的:我想根据控件的值将数据加载到全局变量中。 我不想从子线程更改控件的值。 我永远不会从子线程执行此操作。
因此,仅访问该值,以便可以从数据库中获取相应的数据。
您想要的解决方案应如下所示:
UserContrl1_LOadDataMethod()
{string name = "";if(textbox1.InvokeRequired){textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));}if(name == "MyName"){// do whatever}
}
在尝试切换回控件的线程之前,请在单独的线程中进行认真的处理。 例如:
UserContrl1_LOadDataMethod()
{if(textbox1.text=="MyName") //<<======Now it wont give exception**{//Load data correspondin to "MyName"//Populate a globale variable List<string> which will be//bound to grid at some later stageif(InvokeRequired){// after we've done all the processing, this.Invoke(new MethodInvoker(delegate {// load the control with the appropriate data}));return;}}
}
#5楼
您需要查看Backgroundworker示例:
http://msdn.microsoft.com/zh-cn/library/system.componentmodel.backgroundworker.aspx特别是它如何与UI层交互。 根据您的发布,这似乎可以回答您的问题。
#6楼
在xamarin stuidio之外的visual studio winforms原型项目中为iOS-Phone monotouch应用程序控制器编程时,我发现需要这样做。 我希望尽可能在VS over xamarin Studio上进行编程,我希望控制器与电话框架完全分离。 通过这种方式为其他框架(如Android和Windows Phone)实现此功能,以后使用会容易得多。
我想要一种解决方案,其中GUI可以响应事件,而无需负担每次单击按钮后的交叉线程切换代码的负担。 基本上让类控制器进行处理,以使客户端代码保持简单。 您可能在GUI上发生了许多事件,好像您可以在类中的一个地方处理它一样,这样会更干净。 我不是多学科的专家,请告诉我这是否有缺陷。
public partial class Form1 : Form
{private ExampleController.MyController controller;public Form1(){ InitializeComponent();controller = new ExampleController.MyController((ISynchronizeInvoke) this);controller.Finished += controller_Finished;}void controller_Finished(string returnValue){label1.Text = returnValue; }private void button1_Click(object sender, EventArgs e){controller.SubmitTask("Do It");}
}
GUI窗体不知道控制器正在运行异步任务。
public delegate void FinishedTasksHandler(string returnValue);public class MyController
{private ISynchronizeInvoke _syn; public MyController(ISynchronizeInvoke syn) { _syn = syn; } public event FinishedTasksHandler Finished; public void SubmitTask(string someValue){System.Threading.ThreadPool.QueueUserWorkItem(state => submitTask(someValue));}private void submitTask(string someValue){someValue = someValue + " " + DateTime.Now.ToString();System.Threading.Thread.Sleep(5000);
//Finished(someValue); This causes cross threading error if called like this.if (Finished != null){if (_syn.InvokeRequired){_syn.Invoke(Finished, new object[] { someValue });}else{Finished(someValue);}}}
}
#7楼
UI中的线程模型
请阅读UI应用程序中的线程模型以了解基本概念。 链接导航到描述WPF线程模型的页面。 但是,Windows窗体使用相同的想法。
UI线程
- 只有一个线程(UI线程)被允许访问System.Windows.Forms.Control及其子类成员。
- 尝试从与UI线程不同的线程访问System.Windows.Forms.Control的成员将导致跨线程异常。
- 由于只有一个线程,因此所有UI操作都作为工作项排队到该线程中:
- 如果没有用于UI线程的工作,则存在与非UI相关的计算可以使用的空闲间隙。
- 为了使用提到的差距,请使用System.Windows.Forms.Control.Invoke或System.Windows.Forms.Control.BeginInvoke方法:
BeginInvoke和Invoke方法
- 被调用的方法的计算开销应该和事件处理程序方法的计算开销一样小,因为在那里使用了UI线程-负责处理用户输入的线程。 无论这是System.Windows.Forms.Control.Invoke还是System.Windows.Forms.Control.BeginInvoke 。
- 要执行计算昂贵的操作,请始终使用单独的线程。 由于.NET 2.0 BackgroundWorker致力于在Windows窗体中执行计算昂贵的操作。 然而,在新的解决方案所描述的,你应该使用异步的await模式在这里 。
- 仅使用System.Windows.Forms.Control.Invoke或System.Windows.Forms.Control.BeginInvoke方法来更新用户界面。 如果将它们用于繁重的计算,则您的应用程序将阻止:
调用
- System.Windows.Forms.Control.Invoke导致单独的线程等待被调用的方法完成:
BeginInvoke
- System.Windows.Forms.Control.BeginInvoke不会导致单独的线程等待被调用的方法完成:
代码解决方案
阅读有关问题的答案如何从C#中的另一个线程更新GUI? 。 对于C#5.0和.NET 4.5,推荐的解决方案在此处 。
#8楼
例如,要从UI线程的控件获取文本:
Private Delegate Function GetControlTextInvoker(ByVal ctl As Control) As StringPrivate Function GetControlText(ByVal ctl As Control) As StringDim text As StringIf ctl.InvokeRequired Thentext = CStr(ctl.Invoke(New GetControlTextInvoker(AddressOf GetControlText), ctl))Elsetext = ctl.TextEnd IfReturn text
End Function
#9楼
与以前的答案相同,但增加的内容非常简短,可以使用所有Control属性,而不会出现跨线程调用异常。
辅助方法
/// <summary>
/// Helper method to determin if invoke required, if so will rerun method on correct thread.
/// if not do nothing.
/// </summary>
/// <param name="c">Control that might require invoking</param>
/// <param name="a">action to preform on control thread if so.</param>
/// <returns>true if invoke required</returns>
public bool ControlInvokeRequired(Control c, Action a)
{if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate{a();}));else return false;return true;
}
样品用量
// usage on textbox
public void UpdateTextBox1(String text)
{//Check if invoke requied if so return - as i will be recalled in correct threadif (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;textBox1.Text = ellapsed;
}//Or any control
public void UpdateControl(Color c, String s)
{//Check if invoke requied if so return - as i will be recalled in correct threadif (ControlInvokeRequired(myControl, () => UpdateControl(c, s))) return;myControl.Text = s;myControl.BackColor = c;
}
#10楼
我发现检查和调用代码过于冗长和不必要,需要在与表单相关的所有方法中乱扔垃圾。 这是一个简单的扩展方法,可让您完全消除它:
public static class Extensions
{public static void Invoke<TControlType>(this TControlType control, Action<TControlType> del) where TControlType : Control{if (control.InvokeRequired)control.Invoke(new Action(() => del(control)));elsedel(control);}
}
然后,您可以简单地执行以下操作:
textbox1.Invoke(t => t.Text = "A");
没有更多的混乱-简单。
#11楼
按照最简单的方法(我认为),可以从另一个线程修改对象:
using System.Threading.Tasks;
using System.Threading;namespace TESTE
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){Action<string> DelegateTeste_ModifyText = THREAD_MOD;Invoke(DelegateTeste_ModifyText, "MODIFY BY THREAD");}private void THREAD_MOD(string teste){textBox1.Text = teste;}}
}
#12楼
使用Async / Await和回调的新外观。 如果将扩展方法保留在项目中,则只需要一行代码。
/// <summary>
/// A new way to use Tasks for Asynchronous calls
/// </summary>
public class Example
{/// <summary>/// No more delegates, background workers etc. just one line of code as shown below/// Note it is dependent on the XTask class shown next./// </summary>public async void ExampleMethod(){//Still on GUI/Original Thread here//Do your updates before the next line of codeawait XTask.RunAsync(() =>{//Running an asynchronous task here//Cannot update GUI Thread here, but can do lots of work});//Can update GUI/Original thread on this line}
}/// <summary>
/// A class containing extension methods for the Task class
/// Put this file in folder named Extensions
/// Use prefix of X for the class it Extends
/// </summary>
public static class XTask
{/// <summary>/// RunAsync is an extension method that encapsulates the Task.Run using a callback/// </summary>/// <param name="Code">The caller is called back on the new Task (on a different thread)</param>/// <returns></returns>public async static Task RunAsync(Action Code){await Task.Run(() =>{Code();});return;}
}
您可以向Extension方法添加其他内容,例如将其包装在Try / Catch语句中,允许调用方告诉它完成后返回哪种类型,并向调用方进行异常回调:
添加尝试捕获,自动异常记录和回调
/// <summary>/// Run Async/// </summary>/// <typeparam name="T">The type to return</typeparam>/// <param name="Code">The callback to the code</param>/// <param name="Error">The handled and logged exception if one occurs</param>/// <returns>The type expected as a competed task</returns>public async static Task<T> RunAsync<T>(Func<string,T> Code, Action<Exception> Error){var done = await Task<T>.Run(() =>{T result = default(T);try{result = Code("Code Here");}catch (Exception ex){Console.WriteLine("Unhandled Exception: " + ex.Message);Console.WriteLine(ex.StackTrace);Error(ex);}return result;});return done;}public async void HowToUse(){//We now inject the type we want the async routine to return!var result = await RunAsync<bool>((code) => {//write code here, all exceptions are logged via the wrapped try catch.//return what is neededreturn someBoolValue;}, error => {//exceptions are already handled but are sent back here for further processing});if (result){//we can now process the result because the code above awaited for the completion before//moving to this statement}}
#13楼
同样的问题:如何从C中的另一个线程更新GUI
两种方式:
在e.result中返回值,并使用它在backgroundWorker_RunWorkerCompleted事件中设置文本框值
声明一些变量以将这些类型的值保存在单独的类中(它将用作数据持有者)。 创建此类的静态实例,您可以通过任何线程访问它。
例:
public class data_holder_for_controls
{//it will hold value for your labelpublic string status = string.Empty;
}class Demo
{public static data_holder_for_controls d1 = new data_holder_for_controls();static void Main(string[] args){ThreadStart ts = new ThreadStart(perform_logic);Thread t1 = new Thread(ts);t1.Start();t1.Join();//your_label.Text=d1.status; --- can access it from any thread }public static void perform_logic(){//put some code here in this functionfor (int i = 0; i < 10; i++){//statements here}//set result in status variabled1.status = "Task done";}
}
#14楼
我在FileSystemWatcher
遇到了这个问题,发现以下代码解决了该问题:
fsw.SynchronizingObject = this
控件然后使用当前的表单对象来处理事件,因此将在同一线程上。
#15楼
这不是解决此错误的推荐方法,但是您可以快速消除它,它将起作用。 对于原型或演示,我更喜欢这样做。 加
CheckForIllegalCrossThreadCalls = false
在Form1()
构造函数中。
#16楼
我知道现在为时已晚。 但是,即使在今天,如果您仍然无法访问跨线程控件? 这是迄今为止最短的答案:P
Invoke(new Action(() =>{label1.Text = "WooHoo!!!";}));
这就是我从线程访问任何表单控件的方式。
#17楼
this.Invoke(new MethodInvoker(delegate{//your code here;}));
#18楼
动作y; //在类内部声明
label1.Invoke(y =()=> label1.Text =“ text”);
#19楼
跨线程操作有两个选项。
Control.InvokeRequired Property
第二个是使用
SynchronizationContext Post Method
Control.InvokeRequired仅在从Control类继承的工作控件上可用,而SynchronizationContext可以在任何地方使用。 一些有用的信息如下链接
跨线程更新用户界面| 。净
使用SynchronizationContext的跨线程更新UI 。净
#20楼
只需使用以下命令:
this.Invoke((MethodInvoker)delegate{YourControl.Property= value; // runs thread safe});
#21楼
针对UI跨线程问题的最干净(也是正确的)解决方案是使用SynchronizationContext,请参阅多线程应用程序文章中的“同步对UI的调用 ”,它很好地解释了该问题。
跨线程操作无效:从创建该线程的线程以外的线程访问控件相关推荐
- c# winform InvokeRequired 解决跨线程访问控件
C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它. Windows 窗体中 ...
- c#使用MethodInvoker解决跨线程访问控件
c#使用MethodInvoker解决跨线程访问控件 .net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,有一种方法是禁止编译器对跨线程访问作检查,Control.CheckForIlle ...
- VS2005中,C#中跨线程访问控件问题解决方案
最近我在做一个项目,遇到了跨线程要去访问页面控件.但是总是提示出错,不能在其它线程中修改创建控件的线程的控件的值,后来采用了匿名代理,结果很轻松地解决了.解决过程如下: 首先在窗体上,创建一个list ...
- C#中跨线程访问控件问题解决方案
net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,推荐的解决方法是采用代理,用代理方法来间接操作不是同一线程创建的控件. 第二种方法是禁止编译器对跨线程访问作检查,可以实现访问,但是出不出 ...
- java 线程访问控件_多线程下访问控件的方式
前言 在很多情况下,我们都会使用到多线程,在使用多线程的时候,我们很多时候又会去访问控件,这里面就会出现很多问题!!!我以一个最常见的,我们经常会用到的例子来讲讲,在提高自己水平的同时,希望能给大家带 ...
- Silverlight 2 跨域访问控件与WebService的资料整理
跨域访问控件: In other words, a page retrieved from somehost containing a Silverlight object tag with sour ...
- 【Java 并发编程】线程操作原子性问题 ( 问题业务场景分析 | 使用 synchronized 解决线程原子性问题 )
文章目录 总结 一.原子性问题示例 二.线程操作原子性问题分析 三.使用 synchronized 解决线程原子性问题 总结 原子操作问题 : 线程中 , 对变量副本 count 进行自增操作 , 不 ...
- java 线程访问控件_C#多线程与跨线程访问界面控件的方法
本文实例讲述了C#多线程与跨线程访问界面控件的方法.分享给大家供大家参考.具体分析如下: 在编写WinForm访问WebService时,常会遇到因为网络延迟造成界面卡死的现象.启用新线程去访问Web ...
- winform使用多线程时跨线程访问控件
之前写winform用多线程,在子线程里访问窗体控件需要用委托,由于委托使用不熟练,在网上找的各种方法都觉得太复杂看不懂,后来发现一种写法如下: if (pictureBox1.InvokeRequi ...
最新文章
- Python 中文分词(结巴分词)
- Linux常用命令和vi,gdb的使用
- STL源码剖析 list概述
- 静态优先权和动态优先权
- [网络]------长连接和短连接
- Activiti的历史记录级别
- gdb调试动态链接so
- matplotlib画图一行三个图
- 后缀表达式/逆波兰表达式
- 5.Linux性能诊断 --- 追踪技术
- shell逻辑运算符优先级_linux shell 逻辑运算符、逻辑表达式详解
- 《欲罢不能:刷屏时代如何摆脱行为上瘾》书摘
- 《缠中说禅108课》37:背驰的再分辨
- 基于Python飞机票销售系统 毕业设计-附源码141432
- 磊科路由器信号按键_磊科路由器信号增强怎么设置方法
- 玩转微信 | 炫酷的聊天满屏掉爱心系列,赶紧收藏
- 碳纤维复合材料加固钢筋混凝土的极化作用
- php预览ppt,演示文稿幻灯片有哪四种视图模式?
- dubbo整体设计整理
- 给应届毕业生的建议:萌新程序员找工作的三板斧
热门文章
- HDU 4404 Worms(多边形和圆相交)
- Create-React-App创建antd-mobile开发环境
- Unit01: Servlet基础 、 HTTP协议
- .net ServiceStack.Redis 性能调优
- 如何完全卸载OneDrive (Windows 10 64bit)
- 二十六:Struts2 和 spring整合
- Mybatis 的日志管理
- Eclipse + Spring + maven Building a RESTful Web Service ---需要添加注释
- 物理知识(量子物理)
- 什么是「数独」,简单介绍