单线程的winfom程序中,设置一个控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一个新线程中这么做,比如:
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
//当然也可以用匿名委托写成Thread t = new Thread(SetTextBoxValue);
t.Start("Hello World");
}
void SetTextBoxValue(object obj)
{
this.textBox1.Text = obj.ToString();
}
运行时,会报出一个无情的错误:
线程间操作无效: 从不是创建控件“textBox1”的线程访问它。
究其原因,winform中的UI控件不是线程安全的,如果可以随意在任何线程中改变其值,你创建一个线程,我创建一个线程,大家都来抢着更改"TextBox1"的值,没有任何秩序的话,天下大乱...
解决办法:
1.掩耳盗铃法(Control.CheckForIllegalCrossThreadCalls = false;)--仅Winform有效
using System;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;//这一行是关键
}
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
t.Start("Hello World");
}
void SetTextBoxValue(object obj)
{
this.textBox1.Text = obj.ToString();
}
}
}
设置Control.CheckForIllegalCrossThreadCalls为false,相当于不检测线程之间的冲突,允许各路线程随便乱搞,当然最终TextBox1的值到底是啥难以预料,只有天知道,不过这也是最省力的办法
2.利用委托调用--最常见的办法(仅WinForm有效)
using System;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
delegate void D(object obj);
public Form1()
{
InitializeComponent();
}
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(SetTextBoxValue));
t.Start("Hello World");
}
void SetTextBoxValue(object obj)
{
if (textBox1.InvokeRequired)
{
D d = new D(DelegateSetValue);
textBox1.Invoke(d,obj);
}
else
{
this.textBox1.Text = obj.ToString();
}
}
void DelegateSetValue(object obj)
{
this.textBox1.Text = obj.ToString();
}
}
}
3.利用SynchronizationContext上下文 -- 最神秘的方法(Winform/Silverlight能用)
之所以说它神秘,是因为msdn官方对它的解释据说也是不清不楚
using System;
using System.Threading;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnSet_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ParameterizedThreadStart(Run));
MyPram _p = new MyPram() { context = SynchronizationContext.Current, parm = "Hello World" };
t.Start(_p);
}
void Run(object obj)
{
MyPram p = obj as MyPram;
p.context.Post(SetTextValue, p.parm);
}
void SetTextValue(object obj)
{
this.textBox1.Text = obj.ToString();
}
}
public class MyPram
{
public SynchronizationContext context { set; get; }
public object parm { set; get; }
}
}
4.利用BackgroundWorker --最偷懒的办法(Winform/Silverlight通用)
BackgroundWorker会在主线程之外,另开一个后台线程,我们可以把一些处理放在后台线程中处理,完成之后,后台线程会把结果传递给主线程,同时结束自己。
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace ThreadTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnSet_Click(object sender, EventArgs e)
{
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync("Hello World");
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
e.Result = e.Argument;//这里只是简单的把参数当做结果返回,当然您也可以在这里做复杂的处理后,再返回自己想要的结果(这里的操作是在另一个线程上完成的)
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了
this.textBox1.Text = e.Result.ToString();
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}
5.Dispatcher.BeginInvoke--Silverlight的独门秘籍
代码
using System.Threading;
using System.Windows.Controls;
using System.Windows.Input;
namespace ThreadTest
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Thread t = new Thread(SetTextValue);
t.Start("Hello World");
}
void SetTextValue(object text)
{
this.Dispatcher.BeginInvoke(() => { this.txt.Text = text.ToString(); });
}
}
}
分享到:
相关推荐
WinForm C# 多线程编程并更新界面(UI)
多个线程竞争读写同一个资源往往会造成意想不到的意外结果,UI界面也是一种资源,所以跨线程修改UI界面往往被加以限制。这里采用Task + MethodInvoker的方式完成跨线程对UI对的修改。
C#WinForm,多线程快速刷新界面。没什么高级技术。只希望能解决一下需要多线程与UI快速交互的朋友的问题。
使用开发工具为VS2013;.net:4.0 多线程异步刷新ui界面,实时获取任务进度并进行反馈。
使用delegate和Invoke,使用delegate和BeginInvoke,使用BackgroundWorker组件,使用SynchronizationContext组件
Winform实现多线程异步更新UI(进度及状态信息) 实例代码
C#多线程解决界面卡死问题的完美解决方案,简化了之前的写法,更容易记住和理解 C#多线程解决界面卡死问题的完美解决方案,简化了之前的写法,更容易记住和理解
在c#中使用多线程很方便只需要使用System.Threading.Thread的一个实例的Start方法就行了,但是如何实现多线程之间的交互就不是那么简单。本文实现了用子线程去处理数据,并实时更新主线程的ui状态了。下面就开始一...
主要介绍了C#子线程更新UI控件的方法,在桌面应用程序中控制UI界面有着不错的实用价值,需要的朋友可以参考下
C#经常会遇到UI线程被占用导致的界面卡顿,控件反应缓慢,局部停顿导致全界面停顿,这都是因为单一UI线程导致的,现在使用C#自动生成winform都是单一UI线程,想要多UI线程只能自己手动添加
winform 编写工具,多线程处理数据工具模版,实现多线程异步更新UI等操作
最近收集的VB.Net-C#多线程Thread-代理委托delegate编程。文章列表: c#.net多线程同步.txt ...浅述WinForm多线程编程与Control.Invoke的应用.txt 微软.Net开发中的多线程编程总结.txt 线程中的参数传递.txt
如何在winform程序中,让界面不再卡死。 关于委托和AsyncCallback的使用。 界面卡死的原因是因为耗时任务的计算占用了主线程,导致主界面没有办法进行其它操作,比如拖动。造成界面卡死的现象。我们只需要把耗时任务...
因为该累加过程比较耗时,如果直接在UI线程中进行,那么当前窗口将出现假死。为了有更好的用户体验,程序启动一个新的线程来单独执行该计算,然后每隔200毫秒读取一次累加结果,并把结果显示到文本框下方的label控件...
原文地址:https://blog.csdn.net/qq_38693757/article/details/124659970
Thread 达到跨线程更新UI 虽然使用Dispatcher.Invoke 和模拟winform 里面的DoEvent 但是运行中关闭还是会有异常,而且耗资源高; 第二种 : DispatcherTimer 失败:UI还是会卡顿; 第三种 : Timer 建议使用、资源...
DevExpress的GridView动态绑定数据集,多线程动态修改UI自动刷新,输出中打印运行状态等
使用System.Threading.Timer 使用System.Timers.Timer 分别举例这两个类来实现多线程的时钟,不会阻塞UI线程 例子是Winform的