zoukankan      html  css  js  c++  java
  • 应用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

  • 相关阅读:
    mysql数据库存放路径
    mysql 5.5安装不对容易出现问题
    bean的scope属性
    spring四种依赖注入方式
    spring依赖注入(反转控制)
    Tomcat 安装错误
    synchronized详解
    git tag命令
    vjson.hpp
    cmake添加版本号
  • 原文地址:https://www.cnblogs.com/Winston/p/1345936.html
Copyright © 2011-2022 走看看