“线程间操作无效: 从不是创建控件的线程访问它”
分析C#Winform编程中,不支持跨线程直接更新UI控件。
解决方案 方案1:设置窗体属性,取消线程安全检查。缺点:非线程安全,建议不使用
//指定不再捕获对错误线程的调用 Control.CheckForIllegalCrossThreadCalls = false;方案2:使用上下文SynchronizationContext
///方案3:使用Invoke/BeginInvoke方法更新/// 第一步:UI线程的同步上下文 /// SynchronizationContext m_SyncContext = null; public Form1() { InitializeComponent(); //获取UI线程同步上下文 m_SyncContext = SynchronizationContext.Current; } //第二步:定义更新UI控件的方法 ////// 更新文本框内容的方法 /// /// private void SetTextSafePost(object text) { this.textBox1.Text = text.ToString(); } //第三步:定义线程的调用方法 ////// 线程调用方法 /// private void ThreadProcSafePost() { //在线程中更新UI(通过UI线程同步上下文m_SyncContext) m_SyncContext.Post(SetTextSafePost, "This text was set safely by SynchronizationContext-Post."); }
缺点:在Lock中无法包含Invoke/BeginInvoke,造成死锁。
//1、直接调用Invoke
this.Invoke(new Action(() =>{ //插入代码 }));
//2、使用委托与Invoke // 第一步:定义委托类型 // 将text更新的界面控件的委托类型 delegate void SetTextCallback(string text); //第二步:定义更新UI控件的方法 ///方案4:修改线程类型,使用BackgroundWorker进行安全访问/// 更新文本框内容的方法 /// /// private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired)//如果调用控件的线程和创建创建控件的线程不是同一个则为True { while (!this.textBox1.IsHandleCreated) { //解决窗体关闭时出现“访问已释放句柄“的异常 if (this.textBox1.Disposing || this.textBox1.IsDisposed) return; } SetTextCallback d = new SetTextCallback(SetText); this.textBox1.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }
private BackgroundWorker m_BackgroundWorker;// 申明后台对象
public MainWindow()
{
InitializeComponent();
m_BackgroundWorker = new BackgroundWorker(); // 实例化后台对象
m_BackgroundWorker.WorkerReportsProgress = true; // 设置可以通告进度
m_BackgroundWorker.WorkerSupportsCancellation = true; // 设置可以取消
m_BackgroundWorker.DoWork += new DoWorkEventHandler(DoWork); //执行任务
m_BackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(UpdateProgress); //更新控件
m_BackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CompletedWork);//任务完成
m_BackgroundWorker.RunWorkerAsync(this);执行
}
//线程执行程序
void DoWork(object sender, DoWorkEventArgs e)
{
//在线程中更新UI(通过ReportProgress方法)
m_BackgroundWorker.ReportProgress(50, "This text was set safely by BackgroundWorker.");
}
void UpdateProgress(object sender, ProgressChangedEventArgs e)
{
this.textBox1.Text = e.UserState.ToString();
}
void CompletedWork(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error");
}
else if (e.Cancelled)
{
MessageBox.Show("Canceled");
}
else
{
MessageBox.Show("Completed");
}
}
参考博文:
C# Winform 跨线程更新UI控件常用方法汇总



