zoukankan      html  css  js  c++  java
  • ArcEngine多线程开发

      一、前言

      GIS应用开发中,会遇到计算量大耗时长的操作,如果使用单线程开发则UI界面会卡死,这种情况是令人抓狂的。为了实现执行某操作时UI界面保持响应,我们可以使用多线程开发。阅读这篇文章之前需要先了解同步和异步、多线程、STA和MTA、委托(也有资料翻译为“代理”)等相关内容。

      二、AE多线程开发的主要障碍

      AO对象是STA对象,无法在线程间相互传递/共享(什么是STA对象可自行百度,这篇博客中举的例子很好)。同时,AE开发时线程会被标记为STA线程,STA线程之间相互传递的对象必须是简单类型或托管类型。针对此问题,我们可以将AO对象序列化为字符串,将序列化得到的字符串在STA进程间相互传递,在使用时将序列化字符串反序列化为AO对象(AO对象序列化和反序列化可以查看这篇博客),然后执行相关操作。

      三、多线程开发示例

      以坡度计算为例,主要使用的接口和类包括:图层数据相关(IRasterLayer、IGeoDataset、IName、IRasterDataset);分析操作相关(ISurfaceOp);线程类(Thread)

      思路:

      1、声明一个委托用于回调操作结果,编写业务操作函数和操作结果回调函数

      2、实例化工作线程,并将工作线程的ApartmentState设置为STA

      3、从MapControl中获取指定名称的栅格图层,将栅格图层数据源Name对象序列化为字符串传递给工作线程执行分析

      4、回调显示操作结果

      1.声明委托

          //回调委托
          private delegate void SlopeResultCallback(string item);

      2.编写业务操作函数和操作结果回调函数

            /// <summary>
            /// 业务函数,用于执行相关操作
            /// </summary>
            private void Slope(object SerStrOfArcObj)
            {
                //反序列化为Name对象
                IName pName = DeSerialzed(SerStrOfArcObj as string) as IName;
                //获取数据源Geodataset
                IGeoDataset pGeoDs = pName.Open() as IGeoDataset;
    
                //实例化RasterSurfaceOp类
                //可使用IRasterAnalysisEnvironment接口设置分析环境
                //此处只为了演示多线程开发方式,未设置分析环境,读者可根据实际需求自行设置分析环境
                ISurfaceOp pSurfaceOp = new RasterSurfaceOpClass();
                //坡度分析得到分析结果,可使用ISaveAs2接口报错分析结果到指定目录
                IGeoDataset pSlopeRes = pSurfaceOp.Slope(pGeoDs, esriGeoAnalysisSlopeEnum.esriGeoAnalysisSlopeDegrees);
                
                //获取栅格数据集,创建栅格图层
                IRasterDataset pRstDs = (pSlopeRes as IRasterBandCollection).Item(0).RasterDataset;
                IRasterLayer pRstLyr = new RasterLayerClass();
                pRstLyr.CreateFromDataset(pRstDs);
    
                //将pRstLyr序列化为字符串传递给回调函数,将结果更新的UI界面中(MapControl)显示
                string pRstLyrToStr = Serialzed(pRstLyr);
                AddSlopeResultToMap(pRstLyrToStr);  
            }
    
            /// <summary>
            /// 回调函数,更新UI
            /// </summary>
            /// <param name="item"></param>
            private void AddSlopeResultToMap(string item)
            {
                if (this.axMapControl1.InvokeRequired)
                {
                    SlopeResultCallback d = new SlopeResultCallback(AddSlopeResultToMap);
                    this.Invoke(d, new object[] { item });  //切换到UI线程执行委托,this.Invoke()的用法可自行查阅相关资料
                }
                else
                {
                    //反序列化
                    IRasterLayer plyr = DeSerialzed(item) as IRasterLayer;
                    this.axMapControl1.AddLayer(plyr as ILayer);  
                }
            }

      3.实例化工作线程并设置为STA,在工作线程里运行业务操作函数

            /// <summary>
            /// 执行坡度分析
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSlope_Click(object sender, EventArgs e)
            {
                //获取栅格图层,然后获取数据源Name对象
                IRasterLayer pRstLyr = GetLayerByName(axMapControl1, "宋庆国.jpg") as IRasterLayer;
                if (pRstLyr == null)
                    return;
                IName pName = (pRstLyr as IDataLayer).DataSourceName;
    
                //将Name对象序列化为字符串
                string pNameToStr = Serialzed(pName);
    
                //实例化工作线程,并将工作线程设置为STA模式
                Thread t = new Thread(new ParameterizedThreadStart(Slope));
                t.SetApartmentState(ApartmentState.STA);
                //把序列化得到的字符串传递给工作线程
                t.Start(pNameToStr);
            }

      4.完整代码

    using ESRI.ArcGIS.Carto;
    using ESRI.ArcGIS.Controls;
    using ESRI.ArcGIS.DataSourcesFile;
    using ESRI.ArcGIS.DataSourcesRaster;
    using ESRI.ArcGIS.esriSystem;
    using ESRI.ArcGIS.GeoAnalyst;
    using ESRI.ArcGIS.Geodatabase;
    using Microsoft.Win32;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {  
        public partial class Form1 : Form
        {
            //回调委托
            private delegate void SlopeResultCallback(string item);
    
            public Form1()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 回调函数,更新UI
            /// </summary>
            /// <param name="item"></param>
            private void AddSlopeResultToMap(string item)
            {
                if (this.axMapControl1.InvokeRequired)
                {
                    SlopeResultCallback d = new SlopeResultCallback(AddSlopeResultToMap);
                    this.Invoke(d, new object[] { item });  //切换到UI线程执行委托,this.Invoke()的用法可自行查阅相关资料
                }
                else
                {
                    //反序列化
                    IRasterLayer plyr = DeSerialzed(item) as IRasterLayer;
                    this.axMapControl1.AddLayer(plyr as ILayer);  
                }
            }
    
            /// <summary>
            /// 业务函数,用于执行相关操作
            /// </summary>
            private void Slope(object SerStrOfArcObj)
            {
                //反序列化为Name对象
                IName pName = DeSerialzed(SerStrOfArcObj as string) as IName;
                //获取数据源Geodataset
                IGeoDataset pGeoDs = pName.Open() as IGeoDataset;
    
                //实例化RasterSurfaceOp类
                //可使用IRasterAnalysisEnvironment接口设置分析环境
                //此处只为了演示多线程开发方式,未设置分析环境,读者可根据实际需求自行设置分析环境
                ISurfaceOp pSurfaceOp = new RasterSurfaceOpClass();
                //坡度分析得到分析结果,可使用ISaveAs2接口报错分析结果到指定目录
                IGeoDataset pSlopeRes = pSurfaceOp.Slope(pGeoDs, esriGeoAnalysisSlopeEnum.esriGeoAnalysisSlopeDegrees);
                
                //获取栅格数据集,创建栅格图层
                IRasterDataset pRstDs = (pSlopeRes as IRasterBandCollection).Item(0).RasterDataset;
                IRasterLayer pRstLyr = new RasterLayerClass();
                pRstLyr.CreateFromDataset(pRstDs);
    
                //将pRstLyr序列化为字符串传递给回调函数,将结果更新的UI界面中(MapControl)显示
                string pRstLyrToStr = Serialzed(pRstLyr);
                AddSlopeResultToMap(pRstLyrToStr);  
            }
    
            /// <summary>
            /// 执行坡度分析
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnSlope_Click(object sender, EventArgs e)
            {
                //获取栅格图层,然后获取数据源Name对象。axMapControl1是窗体上的地图控件
                IRasterLayer pRstLyr = GetLayerByName(axMapControl1, "宋庆国.jpg") as IRasterLayer;
                if (pRstLyr == null)
                    return;
                IName pName = (pRstLyr as IDataLayer).DataSourceName;
    
                //将Name对象序列化为字符串
                string pNameToStr = Serialzed(pName);
    
                //实例化工作线程,并将工作线程设置为STA模式
                Thread t = new Thread(new ParameterizedThreadStart(Slope));
                t.SetApartmentState(ApartmentState.STA);
                //把序列化得到的字符串传递给工作线程
                t.Start(pNameToStr);
            }
    
            /// <summary>
            /// 根据名字获取图层
            /// </summary>
            /// <param name="axMapControl1"></param>
            /// <param name="lyrName"></param>
            /// <returns></returns>
            private ILayer GetLayerByName(AxMapControl axMapControl1, string lyrName)
            {
                for (int i = 0; i < axMapControl1.LayerCount; i++)
                {
                    ILayer tempLyr = axMapControl1.get_Layer(i);
                    if (tempLyr.Name == lyrName)
                        return tempLyr;
                }
                return null;
            }
    
            /// <summary>
            /// 序列化实现IPersistStream接口的对象
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            private string Serialzed(object obj)
            {
                string serialzedAsXMLString = null;
                try
                {
                    ///序列化
                    IXMLPersistedObject xmlWrapper = new XMLPersistedObjectClass();
                    xmlWrapper.Object = obj;
                    IXMLSerializer xmlSerializer = new XMLSerializerClass();
                    serialzedAsXMLString = xmlSerializer.SaveToString(xmlWrapper, null, null);              
                }
                catch { }
                return serialzedAsXMLString;
            }
    
            /// <summary>
            /// 反序列化实现IPersistStream接口的对象
            /// </summary>
            /// <param name="serialzedStr"></param>
            /// <returns></returns>
            private object DeSerialzed(string serialzedStr)
            {
                object obj=null;
                try
                {
                    ///反序列化
                    IXMLSerializer xmlSerializer = new XMLSerializerClass();
                    IXMLPersistedObject deWrapper = xmlSerializer.LoadFromString(serialzedStr, null, null) as IXMLPersistedObject;
                    obj = deWrapper.Object;
                }
                catch { }
                return obj;
            }
        }
    }

     注:参考AO帮助中的“Writing multithreaded ArcObjects code”

  • 相关阅读:
    c# 改变FileUpload 上传文件大小
    使用ActiveX读取客户端mac地址
    javascript小技巧
    【2012百度之星/资格赛】H:用户请求中的品牌 [后缀数组]
    POJ1012 约瑟夫环问题[双向循环链表+打表技巧]
    北大ACM题分类
    ACM大量习题题库
    POJ1423 计算出n的阶乘的位数大数问题[Stirling公式]
    ACM训练计划(下)
    POJ2080 角度问题[cmath函数]
  • 原文地址:https://www.cnblogs.com/songqingguo/p/12820152.html
Copyright © 2011-2022 走看看