转自原文ArcGIS Engine 中的多线程使用

一直都想写写AE中多线程的使用,但一直苦于没有时间,终于在中秋假期闲了下来。呵呵,闲话不说了,进入正题!

大家都了解到ArcGIS中处理大数据量时速度是相当的慢,这时如果你的程序是单线程的,那可就让人着急坏了,不知道处理到什么地步,不能操作其他的功能,无奈~~如果在这时你能够想到用多线程技术,那就来试试该如何完成吧。

首先,你得有点VS的多线程经验或学习经验,得知道什么多线程,代理(Delegate)是什么,同步与异步又是什么,等等。这些在VS的帮助文档中都有详细解释,在这里我就不越俎代庖了。我们其中精神去理解ArcGIS中多线程吧。

在ArcgIS中,我们分几个部分阐述多线程。

1、何时使用多线程

在创建多线程应用程序是应注意两点:线程的安全性和线程的伸缩性。线程安全对于所有的对象都是非常重要的,但是仅仅只有线程安全的对象并不意味着成功创建多线程应用程序,或者说线程安全能够提高应用程序的性能。

.NET框架允许你在应用程序中能够迅速的创建线程,但是,在编写ArcObjects代码的多线程必须要小心。ArcObjects最根本的结构是组件对象模型(COM)。从这一点来说,编写ArcObjects的多线程的代码需要既了解.NET多线程,又要了解COM多线程模型。

多线程并不总是使你的程序跑的很快,在许多情况下,它还会增加开支和复杂性,这些最终会减慢程序的执行速度。当增加的复杂性是值得的,那么多线程才能使用。一个基本的原则是,如果一个任务可以分解为不同的独立任务时,那这个任务是适合多线程的。

2、ArcObjects线程模型

所有的ArcObjects组件都被标记为单线程单元(STA参考VS帮助文档)。每个STA都限制在一个线程中,但是COM并不限制每个进程中STA的数目。当一个方法调用进入一个STA,它被转移到STA的唯一线程。因此,在STA中的一个对象将一次只接收和处理一个方法调用,它接收的每个方法调用会到达同一线程。

ArcObjects组件是线程安全的,开发者可把他们在多线程环境下使用。对于AO应用程序在多线程环境下有效运行,由AO所使用的线程单元模型,即独立线程,必须加以考虑。该模型的工作原理是消除跨线程通信。一个线程内所有ArcObjects对象的引用应当只与在同一个线程的对象进行通信。

对于此模型的运行,在ArcGIS 9.X中单个对象都被设计为线程唯一,而非进程唯一。在进程中管理多个对象的资源消耗超过由制止跨线程通信所获得的性能提升幅度。

对于扩展ArcGIS系统的开发者,所有对象甚至包括你创造的对象都必须遵循这一规则,孤立线程工作。如果你创建的对象做为开发的一部分,你必须确保它们是线程唯一,而不是进程唯一。线程唯一就是防止跨线程通信,这里ArcGIS Engine中多线程的首要规则。

3、多线程方案

尽管有很多实现多线程应用程序的方式,但以下几种方案是开发者经常使用的方式。

3.1、后台线程执行长事务

当要求需要长事务进行工作时,在后台执行长事务是可取的,并且同时让应用程序灵活的操作其他任务,并让界面处于响应状态。这一操作的例子很多,如:使用FeatureCursor来重复向DataTable装载数据,进行复杂的拓扑计算并写入新的FeatureClass。为了完成这类任务,请记住以下几点:

a. 根据在孤立模型中的线程,你不能在线程之间共享ArcObjects的组件。相反,你需要考虑的是,单个对象都在各自线程中,并在后台线程中,例如所有工厂需要打开FeatureClass,创造新的FeatureClass,设置空间参考等等。

b.传递给线程的所有信息必须是简单类型或托管类型的形式。

c.万一在某种情况下,你要从主线程向工作线程传递ArcObjects组件,可以将对象序列化成字符串,再将字符串传递给目标线程,然后再反序列化还原到对象。例如,你可以使用XmlSerializerClass序列化对象成为字符串,如工作区间(Workspace)连接属性(IPropertySet),再将这一字符串传递给目标线程,然后在工作线程中使用XmlSerializerClass反序列化连接属性。这样,就将连接属性对象在后台再次创造出来,从而避免了跨线程访问。

当运行后台线程,你能够在用户界面了解任务的进度。

3.2、实施单机ArcObjects的应用程序

正如微软开发人员网络(MSDN)网站上所说,“在.NET Framework版本2.0中,如果线程的单元状态在启动前尚未确定,新的线程就初始化为ApartmentState.MTA。主应用程序线程默认初始化为ApartmentState.MTA。您不能通过设置代码的第一行Thread.ApartmentState属性再设置主应用程序线程到ApartmentState.STA。而应使用STAThreadAttribute代替。”

作为ArcObjects的开发人员,这意味着,如果您的应用程序不被视为一个单一线程应用程序初始化的,.NET框架将为所有的ArcObjects创建一个特殊的单线程单元(STA)线程,因为他们被标记STA。这将导致对每一个从应用程序调用ArcObjects的线程切换到这个特定的线程上来。反过来,这迫使ArcObjects组件合在一起调用,并最终以COM组件调用可能慢了约50倍。幸运的是,这可避免通过简单地标记主要功能为[STAThread]。

3.3、使用托管线程池和BackgroundWorker的线程

线程池线程都是后台线程。线程池通过提供一个由系统管理的应用程序线程池使你使用线程更有效率。利用为每个任务创建一个新线程的线程池的优点是线程创建和销毁的开销是可忽略的,它可以带来更好的性能和更好的系统稳定性。

然而,设计的所有ThreadPool线程是在多线程单元(MTA),因此不应该被用来运行ArcObjects,它们是单线程单元。若要解决此问题,您有几种选择。一个是实现一个专用ArcObjects的线程,它被标记为STAThread并委派每个从MTA线程调用这个专用ArcObjects线程。另一种解决方案是使用自定义的STA线程池的实现,如标记为STA线程的线程数组来运行 ArcObjects。

3.4、同步运行线程的并发执行

在许多情况下,您必须同步执行的并发运行的线程。通常,你要等待一个或多个线程完成他们的任务,当一定条件下得到满足,一个等待线程的信号恢复其任务,条件如:测试是给定线否程激活和运行,改变线程优先级,或给予其他一些条件。

在.NET中有几种方法来管理运行线程的执行。可用来帮助线程管理的主要几类如下:

System.Threading.Thread;

System.Threading.WaitHandle;

System.Threading.Monitor;

System.Threading.AutoResetEvent and System.Threading.ManualResetEvent。

3.5、在多个线程共享一个托管类型

有时候你的.NET应用程序的底层数据结构将是一个如DataTable或哈希表管理的对象。这些.NET托管对象允许你在多个线程共享数据获取,如线程和主线程渲染他们。但是,您应该咨询MSDN Web站点以验证这一点是否是线程安全的。在许多情况下,一个对象是线程读安全,而写并不安全。有些集合实施同步方法,它提供了一个底层集合的同步包装。

在你的对象被多个线程访问的情况下,根据MSDN关于这种情况的对象线程安全规则,你应该获得一个独占锁。取得这样的独占锁能够完成上面所描述的同步方法,或使用lock语句,它通过获取给定对象的相互排他锁标签一个关键块。它可以确保,如果另一个线程试图访问对象时,它会被阻塞,直到该对象被释放(退出锁)。

3.6、从后台线程更新用户界面

在大多数情况下,您正在使用一个后台线程来执行长时间的操作,你想向用户报告进度,状态,错误或其他与该线程执行的任务相关的信息。这可以通过更新一个应用程序的用户界面控件来实现。但是,在Windows中,窗体控件绑定到一个特定的线程(通常是主线程),并且不是线程安全的。因此,你必须委派,从而结合,任何调用UI控件的线程来控制它的所属。该委托是通过调用Control.Invoke方法,该方法在线程上执行委托,该委托拥有控件的基础窗口句柄。要验证调用者是否必须调用Invoke方法,你可以使用属性Control.InvokeRequired。您必须确保该控件的句柄再尝试调用Control.Invoke或Control.InvokeRequired之前已经创建。

3.7、从一个线程调用ArcObjects而不是主线程

在许多多线程应用程序中,你将需要从不同线程调用AO组件。例如,你可能有一个后台线程来获取Web服务,这反过来,应该增加新的项目到地图显示,响应更改地图,或运行的geoprocessing(gp)的工具来执行某些类型分析。

一个非常常见的情况是从一个计时器事件处理方法调用ArcObjects。计时器的Elapsed事件是在一个线程池的任务提出,这不是一个主线程。然而,它需要使用ArcObjects,这好像是需要跨单元调用。然而,这可以避免处理ArcObjects的组件,就好像AO组件是一个用户界面控件和使用Invoke来调用委派到创建ArcObjects组件的主线程中。因此,没有跨单元调用。

ISynchronizeInvoke接口包括的方法有Invoke,BeginInvoke,和EndInvoke。自己实现这些方法可能是一个艰巨的任务。相反,你应该有你直接从System.Windows.Forms.Control继承的类或者你应该有一个助手类,它继承自控件。要么选择将提供一个简单而有效的对于调用方法的解决方案。

delegate SomethingClassType SomeDelegate(IArray array);
SomeDelegate del = new SomeDelegate(AnotherFunc);//AnotherFunc与SomeDelegate同样的形式
IAsyncResult ireslt = del.BeginInvoke(array, null, null);//异步操作
ProgressbarForm form = new ProgressbarForm();//异步操作中的进度条窗体
form.Show();
System.Windows.Forms.Application.DoEvents();
while (!ireslt.IsCompleted)
{
System.Windows.Forms.Application.DoEvents();
}
SomethingClassType something= del.EndInvoke(ireslt);
form.Close();

以上是理论方面的阐述及一个本人开发过程中的一个代码片段,希望这些能够帮助你完成你的多线程程序。参考的资料如下:Windows MSDN,ESRI 的开发者网站。

ArcGIS Engine 中的多线程使用的更多相关文章

  1. [转载]ArcGIS Engine 中的多线程使用

    ArcGIS Engine 中的多线程使用 原文链接 http://anshien.blog.163.com/blog/static/169966308201082441114173/   一直都想写 ...

  2. ArcGIS Engine 中的多线程使用[转载]

    一直都想写写AE中多线程的使用,但一直苦于没有时间,终于在中秋假期闲了下来.呵呵,闲话不说了,进入正题!         大家都了解到ArcGIS中处理大数据量时速度是相当的慢,这时如果你的程序是单线 ...

  3. ArcGIS Engine中的8种数据访问 (转)

    数据是GIS的基础, 访问数据也是进行任何复杂的空间分析及空间可视化表达的前提.ArcGIS支持的数据格式比较丰富,对不同的数据格式支持的程度也有很大差异.本文主要介绍一下以下八种数据格式在ArcGI ...

  4. ArcGIS Engine中的数据访问

    ArcGIS Engine中的数据访问 数据是GIS的基础, 访问数据也是进行任何复杂的空间分析及空间可视化表达的前提.ArcGIS支持的数据格式比较丰富,对不同的数据格式支持的程度也有很大差异.本文 ...

  5. ArcGIS Engine中加载数据

    ArcGIS Engine中加载数据 http://blog.csdn.net/gisstar/article/details/4206822   分类: AE开发积累2009-05-21 16:49 ...

  6. ArcGIS Engine中的8种数据访问

    数据是GIS的基础, 访问数据也是进行任何复杂的空间分析及空间可视化表达的前提.ArcGIS支持的数据格式比较丰富,对不同的数据格式支持的程度也有很大差异.本文主要介绍一下以下八种数据格式在ArcGI ...

  7. [转] ArcGIS engine中气泡标注的添加、修改

    小生 原文 ArcGIS engine中气泡标注的添加.修改! 你微微地笑着,不同我说什么话.而我觉得,为了这个,我已等待得久了.                                   ...

  8. ArcGIS Engine中空间参照(地理坐标)相关方法总结转

    ArcGIS Engine中空间参照(地理坐标)相关方法总结 来自:http://blog.csdn.net/u011170962/article/details/38776101 1.创建空间参考 ...

  9. ArcGIS Engine中如何获取Map中已经选择的要素呢(转)

    ArcGIS Engine中如何获取Map中已经选择的要素呢   1.使用IEnumFeturea对象获取map中的FeatureSelection,该方法可以获取所有图层的选择要素.IMap中的Fe ...

随机推荐

  1. 【C++】cerr,cout,clog

    http://stackoverflow.com/questions/16772842/what-is-the-difference-between-cout-cerr-clog-of-iostrea ...

  2. SQLite -创建表

    SQLite -创建表 SQLite CREATE TABLE语句用于创建一个新表在任何给定的数据库.创建一个基本表包括表命名和定义其列,每列的数据类型 语法: CREATE TABLE语句的基本语法 ...

  3. 屏幕卫士模式系统APP开发

    利用php的socket编程来直接给接口发送数据来模拟post的操作,(黎灿:I8O..2853..296O 可电可V)线上线下和物流结合在一起,才会产生新零售. 2016年阿里云栖大会上,阿里巴巴马 ...

  4. 爬虫学习之csv读取和存储

    一.读取 该读取主要使用到csv里面的Reader().DictReader()方法,和引用io里面的StringIO进行对字符串进行封装 在处理网上的csv文件方式主要是有一下几方面: • 手动把C ...

  5. JavaEE-05 分页与文件上传

    学习要点 新闻分页显示数据 新闻图片上传 JSP分页显示数据 分页 数据信息较多的的时候一般采用列表显示,方便展示信息: 数据量较大的时候一般采用列表加分页的方式显示,便于阅读. 分页方式:集合或者s ...

  6. Springboot 配置文件与对象之间进行映射之@ConfigurationProperties

    一.将配置文件与实体类绑定1.1.将yaml配置文件的属性映射到Javabean中1.1.1.yaml配置文件注意:键值对的语法,键:之后必须要有空格 1.1.2.Javabean 定义注意:java ...

  7. EBS ORACLE工单齐套率的计算程序

    PROCEDURE Get_wip_accept_item_date(p_use_id in number, p_org_id IN NUMBER, p_start_date IN DATE, p_e ...

  8. luogu P2865 路障

    https://www.luogu.org/problemnew/show/P2865 看到题解好多dijkstra,作为一名钟爱于spfa的蒟蒻看不下去了. 有些spfa要跑两边,代码量要曾长好多( ...

  9. 牛客网 牛可乐发红包脱单ACM赛 C题 区区区间间间

    [题解] 我想到了两种解法. 一种是扫描线+线段树+单调栈. 另一种方法是O(n)的,比较巧妙. 考虑每个数在哪些区间可以作为最小数.最长的区间就是它向左右走,直到有数字比它小,这个可以用单调栈维护. ...

  10. u-boot-2012.04.01移植笔记——支持NAND启动

    1.加入nand读写函数文件: 对于nand的读写我们需要特定的函数,之前写最小bootloader的时候曾写过nand.c文件,我们需要用到它.为了避免混淆,我们先将其改名为init.c,然后拷贝到 ...