理解SynchronizationContext,如何在Winform里面跨线程访问UI控件

SynchronizationContext 类是一个基类,可提供不带同步的自由线程上下文。 此类实现的同步模型的目的是使公共语言运行库内部的异步/同步操作能够针对不同的异步模型采取正确的行为。此模型还简化了托管应用程序为在不同的同步环境下正常工作而必须遵循的一些要求。同步模型的提供程序可以扩展此类并为这些方法提供自己的实现。(来自MSDN)
简而言之就是允许一个线程和另外一个线程进行通讯,SynchronizationContext在通讯中充当传输者的角色。另外这里有个地方需要清楚的,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的。

前戏

之前在winform里面使用多线程或者异步的时候为了避免“访问了不是本线程创建的控件”都是设置窗体的CheckForIllegalCrossThreadCalls为不检查线程冲突,或者检测控件的InvokeRequired?!是的,当然没问题。(解释下,InvokeRequired属性是每个Control对象都具有的属性,它会返回true和false,当是true的时候,表示它在另外一个线程上面,这是必须通过Invoke,BeginInvoke这些方法来调用更新UI对象的方法,当是false的时候,有两种情况,1:位于当前线程上面,可以通过直接去调用修改UI对象的方法,2:位于不同的线程上,不过控件或窗体的句柄不存在。对于句柄是否存在的判断,可以通过IsHandleCreated来获取,如果句柄不存在,是不能调用Invoke...这些方法的,这时候你必须等待句柄的创建)

曾经对异步委托的使用简直上瘾,一天不用就不舒服。结果最近发现SynchronizationContext类真是个好东西啊。可以在窗体里面进行一下封装可以避免使用InvokeRequired去检测UI控件状态的麻烦。这里分享一段测试过程。

曾经以为工作十几年,Winform没啥学头了。其实呵呵了,要想在某个小领域突破是很难的。要成为真正的专家是马虎不得的,真是要活到老学到老。

测试代码

public partial class Form1 : Form
    {

        SynchronizationContext sc = null;
        public Form1()
        {

            InitializeComponent();
            sc = SynchronizationContext.Current;
            this.Load += Form1_Load;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread((object state) =>
            {
                int id = Thread.CurrentThread.ManagedThreadId;

                for (int i = 0; i < 1000; i++)
                {
                    Thread.Sleep(10);
                    sc.Post(UpdateUI, "line " + i.ToString());
                }
            });
            thread.Start();
        }

        /// <summary>
        /// This method is executed on the main UI thread.
        /// </summary>
        private void UpdateUI(object state)
        {
            int id = Thread.CurrentThread.ManagedThreadId;
            SendLogMessage("UpdateUI thread:" + id);
            string text = state as string;
            SendLogMessage(text);
        }

        /// <summary>
        /// UI线程回调方法,访问控件刷新显示
        /// </summary>
        /// <param name="message">消息内容</param>
        private void LogMessageBack(object message)
        {
            rbxMessageInfo.AppendText(DateTime.Now + "->" + message.ToString() + Environment.NewLine);
        }

        /// <summary>
        /// 交给UI线程去排队显示消息
        /// </summary>
        /// <param name="message"></param>
        private void SendLogMessage(string message)
        {
            sc.Post(LogMessageBack, message);
        }
    }

大神对SynchronizationContext的封装

protected internal static System.AsyncCallback SyncCallback(System.AsyncCallback callback)
		{
			System.Threading.SynchronizationContext sc = System.Threading.SynchronizationContext.Current;
			if (sc == null)
			{
				return callback;
			}
			return delegate(System.IAsyncResult asyncResult)
			{
				sc.Post(delegate(object result)
				{
					callback((System.IAsyncResult)result);
				}, asyncResult);
			};
		}
		protected void DataAction(Action action)
		{
			try
			{
				action();
			}
			catch (System.Exception ex)
			{
				OperateLog.OpExcpLog(string.Format("[{0}][{1}]功能操作执行异常,异常原因:{2}", action.Method.GetType().FullName, action.Method.Name, ex.Message));
				System.Windows.Forms.MessageBox.Show(EESException.getException(ex).Message, "异常警告", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Hand);
			}
		}
		protected void BeginAction(MethodDelegate action, object obj, params object[] args)
		{
			EForm.AsyncRequest asyncRequest = new EForm.AsyncRequest(action, obj, args, System.Threading.SynchronizationContext.Current);
			this.inAsyncAction = null;
			this.OnBeginAction(asyncRequest);
			System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(this.OnWaitCallback), asyncRequest);
		}
		protected void BeginAction(object proxy, string command, params object[] args)
		{
			MethodDelegate action = FastInvoke.CreateMethodDelegate(proxy.GetType().GetMethod(command));
			this.BeginAction(action, proxy, args);
		}
		protected void BeginActionWithToolTip(MethodDelegate action, object obj, string toolTip, params object[] args)
		{
			EForm.AsyncRequest asyncRequest = new EForm.AsyncRequest(action, obj, args, System.Threading.SynchronizationContext.Current, toolTip);
			this.inAsyncAction = null;
			this.OnBeginAction(asyncRequest);
			System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(this.OnWaitCallback), asyncRequest);
		}
		protected void BeginActionWithToolTip(object proxy, string command, string toolTip, params object[] args)
		{
			MethodDelegate action = FastInvoke.CreateMethodDelegate(proxy.GetType().GetMethod(command));
			this.BeginAction(action, proxy, new object[]
			{
				toolTip,
				args
			});
		}
		protected virtual void OnBeginAction(EForm.AsyncRequest context)
		{
			if (this.waitingForm.Visible)
			{
				throw new EESException("后台正在执行,请稍候……");
			}
			this.waitingForm.Message = context.ToolTip;
			this.waitingForm.Show();
		}
		protected virtual void OnEndAction(EForm.AsyncResponse context)
		{
			this.waitingForm.Visible = false;
		}
		private void OnWaitCallback(object state)
		{
			EForm.AsyncRequest asyncRequest = (EForm.AsyncRequest)state;
			object result = null;
			System.Exception exception = null;
			try
			{
				result = asyncRequest.Method(asyncRequest.Obj, asyncRequest.Args);
			}
			catch (System.Exception ex)
			{
				exception = ex;
			}
			EForm.AsyncResponse state2 = new EForm.AsyncResponse(asyncRequest.Method, result, asyncRequest.Args, exception);
			System.Threading.SynchronizationContext context = asyncRequest.Context;
			if (context != null)
			{
				context.Post(new System.Threading.SendOrPostCallback(this.OnSendOrPostCallback), state2);
			}
		}
		private void OnSendOrPostCallback(object state)
		{
			EForm.AsyncResponse asyncResponse = (EForm.AsyncResponse)state;
			this.OnEndAction(asyncResponse);
			if (asyncResponse.Exception != null)
			{
				System.Windows.Forms.MessageBox.Show(EESException.getException(asyncResponse.Exception).Message);
				return;
			}
			this.OnActionComplete(asyncResponse);
		}
		protected virtual void OnActionComplete(EForm.AsyncResponse context)
		{
		}

这是摘录的项目里面基类窗体关于SynchronizationContext的封装,子类只要使用各种BeginAction就行了,然后在OnActionComplete里面处理访问UI控件的操作

时间: 2024-10-26 13:24:49

理解SynchronizationContext,如何在Winform里面跨线程访问UI控件的相关文章

跨线程访问UI控件时的Lambda表达式

工作中经常会用到跨线程访问UI控件的情况,由于.net本身机制,是不允许在非UI线程访问UI控件的,实际上跨线程访问UI控件还是 将访问UI的操作交给UI线程来处理的, 利用Control.Invoke方法,将操作传递给UI线程,不推荐使用CheckForIllegalCrossThreadCalls = false; Control.Invoke的签名 // // 摘要: // 在拥有此控件的基础窗口句柄的线程上执行指定的委托. // // 参数: // method: // 包含要在控件的线

C# Winform 跨线程更新UI控件常用方法总结(转)

出处:http://www.tuicool.com/articles/FNzURb 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种: 1. 通过UI线程的SynchronizationContext的Post/Send方法更新: 2. 通过UI控件的Invoke/BegainInvoke方法更新: 3. 通过BackgroundWorker取代Thre

C# Winform 跨线程更新UI控件常用方法汇总

C# Winform 跨线程更新UI控件常用方法汇总 概述 C#Winform编程中,跨线程直接更新UI控件的做法是不正确的,会时常出现“线程间操作无效: 从不是创建控件的线程访问它”的异常.处理跨线程更新Winform UI控件常用的方法有4种:1. 通过UI线程的SynchronizationContext的Post/Send方法更新:2. 通过UI控件的Invoke/BeginInvoke方法更新: 3. 通过BackgroundWorker取代Thread执行异步操作:4. 通过设置窗体

(转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-demo 在C# WINFORM的开发中,难免会遇到多线程的开发以提高程序的执行效率.自己刚才开始在做多线程的开发时也遇到了很多这方面的问题,比如:如何使用并实现多线程功能.跨线程更新UI控件等问题.还记得最初使用的是System.Threading命名空间下的Thread类来实现的: C# 1 2 3

wpf(怎么跨线程访问wpf控件)

在编写代码时,我们经常会碰到一些子线程中处理完的信息,需要通知另一个线程(我这边处理完了,该你了). 但是当我们通知WPF的UI线程时需要用到Dispatcher. 首先我们需要想好在UI控件上需要显示什么内容.然后写一个显示UI内容的方法. 以下是代码 private void UIThreaddosomething(string s) //UI线程要做的事情 { //do something //这里也可以做一些其他的事情 Label2.Content = s; ellipse1.Fill=

实现Winform 跨线程安全访问UI控件

在多线程操作WinForm窗体上的控件时,出现"线程间操作无效:从不是创建控件XXXX的线程访问它",那是因为默认情况下,在Windows应用程序中,.NET Framework不允许在一个线程中直接操作另一个线程中的控件(因为访问Windows窗体控件本质上不是线程安全的).微软为了线程安全,窗体上的控件只能通过创建控件的线程来操作控件的数据,也就是只能是UI线程来操作窗体上的控件!可看看Control的Invoke和BeginInvoke 要解决这个问题可以用以下方法: 1.关闭线

C#子线程更新UI控件的方法总结

http://blog.csdn.net/jqncc/article/details/16342121 在winform C/S程序中经常会在子线程中更新控件的情况,桌面程序UI线程是主线程,当试图从子线程直接修改控件属性时会出现“从不是创建控件的线程访问它”的异常提示. 跨线程更新UI控件的常用方法有两种: 1.使用控件自身的invoke/BeginInvoke方法 2.使用SynchronizationContext的Post/Send方法更新 1.使用控件自身的invoke/BeginIn

扩展BindingList,防止增加、删除项时自动更新界面而不出现“跨线程操作界面控件 corss thread operation”异常

在做界面程序时,常常需要一些数据类,界面元素通过绑定等方式显示出数据,然而由于UI线程不是线程安全的,一般都需要通过Invoke等方式来调用界面控件.但对于数据绑定bindingList而言,没法响应listchang事件,导致后端的grid等控件不能更新数据.废了好大的劲终于找到一个UIBindingList,实现线程数据的同步! using System; using System.ComponentModel; using System.Threading; using System.Wi

Winform中子线程访问界面控件时被阻塞解决方案

public partial class WebData_Import : Form { //声明用于访问主界面的委托类型 public delegate void deleGetOrderdata(string info); //声明访问主界面委托类型的变量 public deleGetOrderdata OptGetOrderData; int CompanyID = 0; public WebData_Import() { InitializeComponent(); cmbCompany