ArcEngine多线程开发
Posted songqingguo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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; } } }
以上是关于ArcEngine多线程开发的主要内容,如果未能解决你的问题,请参考以下文章