zoukankan      html  css  js  c++  java
  • SynchronizationContext 学习

    (CLR via C# 3rd Edition)

    在 GUI 程序中 (winform / wpf / silverlight),如果启动新的线程池线程,则在此线程中将不能直接更新 UI. 在 asp.net 中,在处理 client request 的线程中,同时会根据客户端的情况关联相应的 System.Globalization.CultureInfo, 以便服务器端能够使用客户相关的 culture-specific 的数字,日期和时间格式。而且,Web server 能够假定客户所用的身份(identify, 即 System.Security.Principal.IPrincipal), 以控制客户能够访问的资源。但如果在此线程中启动新的线程池线程,则此线程池线程将不再使用客户特定的 CultureInfo 和 Identity.

    SynchronizationContext 类用于解决这些问题。其原型为:

    public class SynchronizationContext {
    	public static SynchronizationContext Current { get; }
    	public virtual void Post(SendOrPostCallback d, object state); // 异步调用
    	public virtual void Send(SendOrPostCallback d, object state); // 同步调用
    }

    对 winform / wpf / silverlight 程序,GUI 线程具有一个相关的 SynchronizationContext 子类。对 winform 来说是 System.Windows.Forms.WindowsFormsSynchronizationContext, 对 wpf 和 silverlight 来说是 System.Windows.Threading.DispatcherSynchronizationContext.

    可以通过 SynchronizationContext.Current 获得此对象。通常,会在发动线程池调用之前的某个时刻,将此对象的引用保存到另一个类变量或静态变量中。然后,在线程池中需要更新 UI 时,就可以通过调用其 Post 方法来实现。

    推荐总是使用 Post 方法,而不要用 Send 方法。因为 Send 方法是同步调用,意味着在线程池线程中调用它时,会阻塞住当前线程,以等待其返回。而当线程池线程被阻塞时,往往会触发线程池调度程序分配一个新的线程,从而增加了不必要的资源耗费,降低性能。

    从实现原理上说,WindowsFormsSynchronizationContext 的 Post 方法调用了 System.Windows.Forms.Control.BeginInvoke 方法,而 Send 方法则是调用了 Invoke 方法(分别对应于异步/同步调用)。类似的,对于 wpf 和 silverlight, System.Threading.DispatcherSynchronizationContext 的 Post 方法则是调用了 System.Windows.Threading.Dispatcher.BeginInvoke 方法,Send 方法调用其 Invoke 方法。

    对 asp.net / xml webservices 程序而言,用于处理客户请求的那个线程池线程,会自动关联上一个 SynchronizationContext 类的子类。其中包含客户的 culture 和 identity 信息。该实例同样可以通过 SynchronizationContext.Current 得到。如果你要新启动一个线程,你同样可以先保存对当前 SynchronizationContext 对象的引用,然后晚一点通过 Post 方法在其上来执行代码,这些代码会在最初用来处理客户请求的同一个线程池线程中执行。

    下面是一个封装,用来简化异步编程后需要在发起线程里执行任务(比如更新 UI)的场景。

    private static AsyncCallback SyncContextCallback(AsyncCallback callback) {
    	// 捕获调用者线程的同步对象
    	var sc = SynchronizationContext.Current;
    
    	// 如果没有同步上下文,则不需要做任何包装,直接返回
    	if (sc == null) return callback;
    	
    	// 返回一个新的委托,当它被调用时,将原始调用转发 post 给捕获的同步上下文对象。并且传递原来的 IAsnycResult 参数。
    	return asyncResult => sc.Post(result => callback((IAsyncResult) result), asyncResult);
    }

    调用例子:

    protected override void OnMouseClick(MouseEventArgs e) {
    	Text = "Web request initiated";
    	var req = WebRequest.Create(“http://rchen.cnblogs.com”);
    	req.BeginGetResponse(SyncContextCallback(ProcessWebResponse), req);
    	base.OnMouseClick(e);
    }
    
    void ProcessWebResponse(IAsyncResult result) {
    	// 因为回调函数被包装过,这里可以直接更新 UI.
    	var req = (WebRequest) result.AsyncState;
    	using (var response = req.EndGetResponse(result)) {
    		Text = "Context length: " + response.ContextLength;
    	}
    }

    ==

  • 相关阅读:
    Java Output流写入包装问题
    SpringBoot项目单元测试不经过过滤器问题
    SpringSecurity集成启动报 In the composition of all global method configuration, no annotation support was actually activated 异常
    JWT jti和kid属性的说明
    Maven 排除依赖
    第五章 基因概念的发现
    第三章 孟德尔遗传的拓展
    第二章 孟德尔遗传
    第一章 引言
    GWAS全基因组关联分析
  • 原文地址:https://www.cnblogs.com/RChen/p/1818535.html
Copyright © 2011-2022 走看看