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
gis字符串是什么///</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()的⽤法可⾃⾏查阅相关资料
{
//反序列化
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 = _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;
///序列化
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”

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。