• 应用SynchronizationContext来实现对threadsafe UI的同步访问的2种不同Programming Model


    前言:

    当有多个threads访问某个thread-safe的资源如UI时,必须有某种mechanism来保证这些thread marshaling。对于需要thread-safe的资源如果没有施加任何措施而被其他thread同时访问的话,会抛出如下类似的invalidoperationException

    Cross-thread operation not valid: Control 'm_CounterLabel' accessed from a thread other than the thread it was created on.

    excpetion screenshot如下:

    Programming Model 1:

    在下面的sample中,MyForm提供了一个叫做允许client访问来获得该UI的MySynchronizationContext的propertity。MySynchronizationContext是通过在在MyForm的Constructor中通过获得当前线程的上下文来初始化的。 MyForm也提供了Counter Property来更新服务端的Windows Forms label. 当然 .Counter只能被占有Form的当前thread来访问。

    source code:

    Service UI

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace UISynchronizationContext_Service
    {
        
    public partial class MyForm : Form
        {
            
    private System.Windows.Forms.Label m_CounterLabel;
            System.Threading.SynchronizationContext m_SynchronizationContext;

            
    public MyForm()
            {
                InitializeComponent();
                m_SynchronizationContext 
    = System.Threading.SynchronizationContext.Current;
                System.Diagnostics.Debug.Assert(m_SynchronizationContext 
    != null);
            }

            
    public System.Threading.SynchronizationContext MySynchronizationContext
            {
                
    get
                {
                    
    return m_SynchronizationContext;
                }
            }

            
    public int Counter
            {
                
    get { return Convert.ToInt32(m_CounterLabel.Text); }
                
    set { m_CounterLabel.Text = value.ToString(); }
            }
            
    private void MyForm_Load(object sender, EventArgs e)
            {

            }
        }
    }

    Service Contract and Implementation

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel;

    namespace UISynchronizationContext_Service
    {
        [ServiceContract]
        
    public interface IFormManager
        {
            [OperationContract(IsOneWay 
    = true)]
            
    void IncrementLabel();
        }

        
    class MyContract:IFormManager
        {
            
    public void IncrementLabel()
            {
                MyForm form 
    = System.Windows.Forms.Application.OpenForms[0as MyForm;
                System.Diagnostics.Debug.Assert(form 
    != null);
                SendOrPostCallback callback 
    = delegate
                {
                    form.Counter
    ++;
                };

                form.MySynchronizationContext.Send(callback, 
    null);
                
            }
        }

        
    static class Program
        {
            
    /// <summary>
            
    /// The main entry point for the application.
            
    /// </summary>
            [STAThread]
            
    static void Main()
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(
    false);

                ServiceHost host 
    = new ServiceHost(typeof(MyContract));
                host.Open();

                Application.Run(
    new MyForm());
                host.Close();
            }
        }
    }

    运行结果:

    Service Updated UI by multiple threads

    #1 Client Calls for service to update service's UI 

     

    #2 Client Calls for service to update service's UI 

    缺点:

    该programming model的defiency在于:服务(service)和UI form过于耦合。很显然,这是一种不好的设计模式。如果在Form中需要更新多个control的话,会非常不方便。 比较理想的设计方法是将Service和UI form decouple,两者不受相互影响。

    Programming Model 2:

    那么,如何实现这样的programming model呢?我们可以自定义thread-safe control。将一些与windows form的同步上下文的要求thread-safe的操作封装在这些所谓的safe control里面,从而将其与service 脱耦。

    例如,我们对上面代码略作修改:

    1. 我们在Form上添加一个m_MyTextBox的引用, 但是其实这个m_MyTextBox是一个MyTexbBox的reference, 其是一个自定义的derived from System.Windows.Forms.Text的safe textbox class。我们将其某些Method或者Propertity进行了Override以便其运行在thread-safe之下:

    以下是MyTextBox这个自定义TextBox的definition

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;

    namespace UISynchronizationContext_Service
    {
        
    class SafeTextBox:TextBox
        {
            SynchronizationContext m_SynchronizationContext 
    = SynchronizationContext.Current;

            
    public override string Text
            {
                
    set
                {
                    SendOrPostCallback setText 
    = delegate(object text)
                    {
                        
    base.Text = text as string;
                    };
                    m_SynchronizationContext.Send(setText, value);
                }
                
    get
                {
                    
    string text = String.Empty;
                    SendOrPostCallback getText 
    = delegate
                    {
                        text 
    = base.Text;
                    };
                    m_SynchronizationContext.Send(getText, 
    null);
                    
    return text;               
                }
            }

           
        }
    }

    然后假设在ServiceContract中新增加一个Operation:

    [OperationContract(IsOneWay = true)]

    void UpdateTextBox(string textBoxValue);

    用来实现对TextBox内Text的修改。

    其Implementation实现如下:

    class MyContract:IFormManager
        {
            
    public void IncrementLabel()
            {
                MyForm form 
    = System.Windows.Forms.Application.OpenForms[0as MyForm;
                System.Diagnostics.Debug.Assert(form 
    != null);
                SendOrPostCallback callback 
    = delegate
                {
                    form.Counter
    ++;
                };

                form.MySynchronizationContext.Send(callback, 
    null);
                
            }

           
    public void UpdateTextBox(string textBoxValue)
            {
                MyForm form 
    = System.Windows.Forms.Application.OpenForms[0as MyForm;
                form.TextBoxValue = textBoxValue;
            }
        }

    就只有一条非常简单的语句!而那些所有对Control的同步上下文操作都被封装在了其safe control中了 :) so cool.

     当然,client代码也作简单修改如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace UISynchronizationContext_Client
    {
        
    class Program
        {
            
    static void Main(string[] args)
            {
                FormManagerClient proxy 
    = new FormManagerClient();
                Console.WriteLine(
    "Press any key to continue processing");
                Console.ReadLine();
                proxy.IncrementLabel();
                
    for (int i = 0; i < 10; i++)
                {
                    proxy.UpdateTextBox(
    "value set from #1 client is " + i.ToString());
                    System.Threading.Thread.Sleep(
    2000);
                }
                Console.WriteLine(
    "Updated Service's UI");
                Console.ReadLine();
            }
        }
    }

    为了看出Service的safe control是如何marshal这些update control的请求的,特意给出了连续10次这样的操作。

    如何有多个不同的client在不断call 这个service,可以看到service端的TextBox中的Text不断的被不同的Client thread Update。

    运行结果:

    Service UI运行结果如下:

     For source code regarding to this article, pls press here to download

  • 相关阅读:
    7月的尾巴,你是XXX
    戏说Android view 工作流程《下》
    “燕子”
    Android开机动画bootanimation.zip
    戏说Android view 工作流程《上》
    ViewController里已连接的IBOutlet为什么会是nil
    My first App "Encrypt Wheel" is Ready to Download!
    iOS开发中角色Role所产生的悲剧(未完)
    UIScrollView实现不全屏分页的小技巧
    Apple misunderstood my app,now my app status changed to “In Review”
  • 原文地址:https://www.cnblogs.com/Winston/p/1345936.html
Copyright © 2020-2023  润新知