`

【Ray谈C#】乌龟爬之SynchronizationContexts

阅读更多
为什么需要SynchronizationContexts

“乌龟爬”这个奇怪的名字,因为在前面那个演示里,我试图用ironruby或者ironpython去控制一只小乌龟,(就是那个三角箭头),通过ironruby的命令让小乌龟前进、后退、左转、右转。



这里存在一个线程同步的问题,敲下一个命令之后,屏幕上的UI物件应该要马上按指令运作(体会一下魔兽世界的宏),所以小乌龟工作在一个UI线程里,而命令窗口工作在一个非UI的线程,如果不加任何处理的话,直接在命令窗口里操作UI物件的引用就会导致一个System.InvalidOperationException: “Object is currently in use elsewhere"(.net 2.0),或者得到一个不可知的状态(.net 1.0)。

应用场景和解决方案

上面提出的一个很常见问题:应用程序有两个线程:线程A和线程B,不过线程B比较特殊,它属于UI线程,当这两个线程同时运行的时候,线程A有个需求:"修改UI对象的属性",这时候如果你是线程A,你会如何去完成需求呢?!可能有几种解决方案。

第一种方式:
    
     在线程A上面直接去操作UI对象,这是线程B说:"线程A,你不知道我的特殊嘛!",然后直接抛给线程A一个InvalidOperationException,线程A得到异常后,一脸的无辜和无奈.....!

第二种方式:

  InvokeRequired?!是的,当然没问题。(InvokeRequired属性是每个Control对象都具有的属性,它会返回true和false,当是true的时候,表示它在另外一个线程上面,这是必须通过Invoke,BeginInvoke这些方法来调用更新UI对象的方法,当是false的时候,有两种情况,1:位于当前线程上面,可以通过直接去调用修改UI对象的方法,2:位于不同的线程上,不过控件或窗体的句柄不存在。对于句柄是否存在的判断,可以通过IsHandleCreated来获取,如果句柄不存在,是不能调用Invoke...这些方法的,这时候你必须等待句柄的创建。有点寒)

这些代码对还在使用.NET 1.0下的朋友还是有所帮助的:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

   public class MyFormControl : Form
   {
      public delegate void AddListItem(String myString);
      public AddListItem myDelegate;
      private Button myButton;
      private Thread myThread;
      private ListBox myListBox;
      public MyFormControl()
      {
         myButton = new Button();
         myListBox = new ListBox();
         ....控件初始化过程,略过....
         myDelegate = new AddListItem(AddListItemMethod);
      }
      static void Main()
      {
         MyFormControl myForm = new MyFormControl();
         myForm.ShowDialog();
      }

      public void AddListItemMethod(String myString)
      {
            myListBox.Items.Add(myString);
      }
      
      private void Button_Click(object sender, EventArgs e)
      {
         myThread = new Thread(new ThreadStart(ThreadFunction));
         myThread.Start();
      }
      
      private void ThreadFunction()
      {
         MyThreadClass myThreadClassObject  = new MyThreadClass(this);
         myThreadClassObject.Run();
      }
   }
   public class MyThreadClass
   {
      MyFormControl myFormControl1;
      public MyThreadClass(MyFormControl myForm)
      {
         myFormControl1 = myForm;
      }
      String myString;

      public void Run()
      {
         for (int i = 1; i <= 5; i++)
         {
            myString = "Step number " + i.ToString() + " executed";
            Thread.Sleep(400);
            // Execute the specified delegate on the thread that owns
            // 'myFormControl1' control's underlying window handle with
            // the specified list of arguments.
            myFormControl1.Invoke(myFormControl1.myDelegate,
                                   new Object[] {myString});
         }
      }
   }


在MyThreadClass类中就存在MyFormControl的引用对象。其实如果这个类放在这里是没有任务不妥之处的,但是如果把MyThreadClass类放在业务层,这时候问题就出现了,从设计角度来说,业务层是不允许和UI有任何关系,所以MyFormControl的引用对象绝对不能存在于MyThreadClass类,但是不让它存在,更新UI控件的需求就满足不了,这种情况下,我们如何做到一种最佳方案呢!?

这种场景就是SynchronizationContext大显身手的时刻。

第三种方案:

根据MSDN的介绍:SynchronizationContext 类是一个基类,可提供不带同步的自由线程上下文。 此类实现的同步模型的目的是使公共语言运行库内部的异步/同步操作能够针对不同的异步模型采取正确的行为。此模型还简化了托管应用程序为在不同的同步环境下正常工作而必须遵循的一些要求。同步模型的提供程序可以扩展此类并为这些方法提供自己的实现。

用大白话说,就是允许一个线程和另外一个线程进行通讯,SynchronizationContext在通讯中充当传输者的角色。另外这里有个地方需要清楚的,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的,当Control对象被创建的同时,SynchronizationContext对象也会被创建并附加到UI线程上。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void mToolStripButtonThreads_Click(object sender, EventArgs e)
    {
        // let's see the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Trace.WriteLine("mToolStripButtonThreads_Click thread: " + id);

        // grab the sync context associated to this
        // thread (the UI thread), and save it in uiContext
        // note that this context is set by the UI thread
        // during Form creation (outside of your control)
        // also note, that not every thread has a sync context attached to it.
        SynchronizationContext uiContext = SynchronizationContext.Current;

        // create a thread and associate it to the run method
        Thread thread = new Thread(Run);

        // start the thread, and pass it the UI context,
        // so this thread will be able to update the UI
        // from within the thread
        thread.Start(uiContext);
    }

    private void Run(object state)
    {
        // lets see the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Trace.WriteLine("Run thread: " + id);

        // grab the context from the state
        SynchronizationContext uiContext = state as SynchronizationContext;

        for (int i = 0; i < 1000; i++)
        {
            // normally you would do some code here
            // to grab items from the database. or some long
            // computation
            Thread.Sleep(10);

            // use the ui context to execute the UpdateUI method,
            // this insure that the UpdateUI method will run on the UI thread.

            uiContext.Post(UpdateUI, "line " + i.ToString());
        }
    }

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


程序首先在Form1窗体的mToolStripButtonThreads_Click事件中,获取当前的SynchronizationContext对象,然后启动另外一个线程,并且将SynchronizationContext对象传递给启动的线程,启动的线程通过SynchronizationContext对象的Post方法来调用一个委托方法UpdateUI,因为UpdateUI是执行在主UI线程上的,所以可以通过它来修改UI上对象的信息。现在我们可以把Control引用给抛弃了。

Send VS Post,以及异常处理

首先看下异常处理的情况
private void Run(object state)
{
    // let's see the thread id
    int id = Thread.CurrentThread.ManagedThreadId;
    Trace.WriteLine("Run thread: " + id);

    // grab the context from the state
    SynchronizationContext uiContext = state as SynchronizationContext;

    for (int i = 0; i < 1000; i++)
    {
        Trace.WriteLine("Loop " + i.ToString());
        // normally you would do some code here
        // to grab items from the database. or some long
        // computation
        Thread.Sleep(10);

        // use the ui context to execute the UpdateUI method, this insure that the
        // UpdateUI method will run on the UI thread.

        try
        {
            uiContext.Send(UpdateUI, "line " + i.ToString());
        }
        catch (Exception e)
        {
            Trace.WriteLine(e.Message);
        }
    }
}
/// <summary>
/// This method is executed on the main UI thread.
/// </summary>
private void UpdateUI(object state)
{
    throw new Exception("Boom");
}


当你运行的时候, 你可能希望在UI线程上面去抛出,但是结果往往出忽你的意料,异常信息都在Run方法的线程上被捕获了。这时候你可能想问:WHY?!

解释之前,我们先看下,Send VS Post的结果:
   Send 方法启动一个同步请求以发送消息
   Post 方法启动一个异步请求以发送消息。   
哈哈,异常处理的答案迎韧而解了吧!
  • 大小: 68.4 KB
分享到:
评论

相关推荐

    V-Ray LUTs 资源管理器 v0.02

    3dsmax插件V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V-Ray LUTs 资源管理器 v0.02V...

    POV.rar_c#光线跟踪_pov_pov ray_光线_追踪算法

    8. **POV-Ray语法**:虽然这个程序是用C#编写的,但可能参考了POV-Ray的语法,以便用户能编写类似于POV-Ray的场景描述文件,描述场景的布局和物体属性。 9. **优化策略**:光线追踪往往需要大量计算,因此,有效的...

    西门子X射线管RAY14中文说明书

    西门子X射线管RAY14中文说明书提供了一系列关于RAY14型X射线管组件的详细信息,包括其设计、特性、技术数据和操作指南。西门子X射线管RAY14适用于普通和数字X射线摄影以及透视工作站的常规检查的诊断。 知识点1:X...

    raytracing

    在C#环境中实现光线追踪,可以利用其强大的面向对象编程特性以及丰富的库支持,来创建复杂的视觉效果。 光线追踪的核心在于计算光线与场景中物体的交互。这个过程包括以下几个关键步骤: 1. **光线生成**:程序...

    c# 点是否在区域内

    在C#编程中,判断一个点是否位于一个多边形区域内是一项常见的几何运算任务,尤其在地理信息系统(GIS)和地图应用中。这个任务通常应用于确定某个地理位置是否在特定的地理边界内,例如行政区划、建筑物轮廓等。在...

    ray-mmd-1.5.2.zip

    "ray-mmd-1.5.2.zip" 是一个压缩包文件,主要针对MMD(MikuMikuDance)的渲染技术。MMD是一款基于3D动画的免费软件,最初设计用于虚拟偶像初音未来及其相关角色的舞蹈和歌曲表演模拟。这个1.5.2版本的"ray-mmd"包含...

    ray-mmd-master.zip

    "ray-mmd-master.zip" 是一个包含 "ray-mmd" 项目的压缩文件,该项目是一个用于三维模型动画渲染的开源工具,特别关注于MikuMikuDance (MMD) 文件格式的支持。MMD是一种流行的3D动画软件,主要用于创建虚拟偶像如...

    ray-master.zip

    "ray-master.zip" 文件是一个压缩包,通常包含了一个名为 "ray-master" 的项目或软件的源代码仓库。Ray 是一个开源的、可扩展的实时计算系统,它在人工智能(AI)和大规模分布式应用中被广泛使用。这个压缩包可能是...

    ray filter 雷达点云地面过滤

    本文将深入探讨“ray filter”算法及其在PCL(Point Cloud Library)中的实现。 首先,了解“ray filter”算法的核心思想。该算法基于射线追踪原理,通过计算每个点到某一参考平面的距离来判断其是否为地面点。如果...

    POV-Ray 3.6.2.dmg

    POV-Ray,全名是Persistence of Vision Raytracer,是一个使用光线跟踪绘制三维图像的开放源代码免费软件。

    ray-2.3.1-cp37-cp37m-win-amd64.whl

    Ray是开源的分布式计算框架,它提供了一种高效、灵活且易于使用的平台,用于构建大规模并行和异步应用程序。这个“ray-2.3.1-cp37-cp37m-win-amd64.whl”文件是Ray的一个Python wheels格式的发行版,适用于Python ...

    C#将文本数据转换成语音进行播报实例

    本项目以"C#将文本数据转换成语音进行播报实例"为主题,适用于.NET Framework 4.0环境,为开发者提供了一个完整的解决方案。 首先,我们要了解C#中实现TTS的核心库——System.Speech。这个库包含了...

    V-Ray64 有效数据资源

    V-Ray 是一款强大的渲染引擎,尤其在3D建模和设计领域中被广泛使用。它为3ds Max等三维软件提供了高级的光照计算和图像渲染功能。V-Ray 64位版本是专为64位操作系统设计的,能够充分利用多核处理器的优势,处理更...

    Ray Match灯光雾技术

    《深入理解Ray Match灯光雾技术在mental ray渲染中的应用》 在三维图形渲染领域,mental ray作为一款强大的渲染引擎,提供了丰富的光照模型和特效技术,其中Ray Match灯光雾技术是其独特且高效的雾化效果解决方案。...

    v-ray 2011

    V-Ray是一款广受欢迎的渲染引擎,专为3D建模软件如3ds Max设计。2011年的版本,即“V-Ray 2011”,是该软件的一个重要里程碑,提供了许多先进的技术和功能,旨在提升3D图像的制作质量和效率。 首先,V-Ray的核心...

    ray tracing in one weekend

    标题《Ray Tracing in One Weekend》和描述表明,这是一本关于光线追踪技术(ray tracing)的入门书籍,作者是Peter Shirley。作者分享了这本书,并说明读者可以在一个周末内学习基础的光线追踪技术。这本书被归类在...

    V-Ray 2.0 3dsmax 2009 64位

    V-Ray是一款广受欢迎的高质量渲染引擎,被广泛应用于建筑可视化、电影、电视和游戏制作等领域。这个特定的版本,V-Ray 2.0 SP1,是专为3ds Max 2009 64位设计的,它提供了一套强大的工具集,使艺术家和设计师能够...

    PG2Ray.rar

    标题 "PG2Ray.rar" 暗示我们讨论的是一个名为 "PG2Ray" 的程序或工具,它被压缩在RAR格式的文件中。RAR是一种常见的压缩格式,用于减少文件大小以便于存储和传输。RAR文件可以包含一个或多个文件,并且通常具有密码...

    机器学习Ray框架论文中文版

    根据提供的文件信息,以下是关于机器学习Ray框架的详细知识点: 1. 强化学习(RL)的应用需求:机器学习的应用正在从传统的监督学习转向强化学习,其中应用程序需要在动态环境中与环境交互并学习。这些应用程序面临...

Global site tag (gtag.js) - Google Analytics