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

 ArcGIS Engine 中的多线程使用

原文连接 http://anshien.blog.163.com/blog/static/169966308201082441114173/       数组

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

        你们都了解到ArcGIS中处理大数据量时速度是至关的慢,这时若是你的程序是单线程的,那可就让人着急坏了,不知道处理到什么地步,不能操做其余的功能,无奈~~若是在这时你可以想到用多线程技术,那就来试试该如何完成吧。安全

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

       在ArcgIS中,咱们分几个部分阐述多线程。数据结构

       一、什么时候使用多线程多线程

在建立多线程应用程序是应注意两点:线程的安全性和线程的伸缩性。线程安全对于全部的对象都是很是重要的,可是仅仅只有线程安全的对象并不意味着成功建立多线程应用程序,或者说线程安全可以提升应用程序的性能。并发

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

多线程并不老是使你的程序跑的很快,在许多状况下,它还会增长开支和复杂性,这些最终会减慢程序的执行速度。当增长的复杂性是值得的,那么多线程才能使用。一个基本的原则是,若是一个任务能够分解为不一样的独立任务时,那这个任务是适合多线程的。异步

二、ArcObjects线程模型ide

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

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

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

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

三、多线程方案

尽管有不少实现多线程应用程序的方式,但如下几种方案是开发者常用的方式。

3.一、后台线程执行长事务

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

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

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

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

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

3.二、实施单机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.三、使用托管线程池和BackgroundWorker的线程

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

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

3.四、同步运行线程的并发执行

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

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

System.Threading.Thread;

System.Threading.WaitHandle;

System.Threading.Monitor;

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

3.五、在多个线程共享一个托管类型

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

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

3.六、从后台线程更新用户界面

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

3.七、从一个线程调用ArcObjects而不是主线程

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

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

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

 1  private void button5_Click(object sender, EventArgs e)  2  {  3  Func();  4  }  5         private delegate int SomeDelegate(Array array);  6 
 7         void Func()  8  {  9             SomeDelegate del = new SomeDelegate(AnotherFunc);//AnotherFunc与SomeDelegate一样的形式
10 
11             IAsyncResult ireslt = del.BeginInvoke(null, null, null);//异步操做
12             ProgressbarForm form = new ProgressbarForm();//异步操做中的进度条窗体
13             form.setProgressBar("提示", "正在处理数据...", 10, 0, 100); 14  form.Show(); 15  System.Windows.Forms.Application.DoEvents(); 16             while (!ireslt.IsCompleted) 17  { 18  System.Windows.Forms.Application.DoEvents(); 19  } 20             int something = del.EndInvoke(ireslt); 21  form.Close(); 22  } 23         private int AnotherFunc(Array array) 24  { 25             int i = 0; 26             while (true) 27  { 28                 i++; 29                 Thread.Sleep(10); 30                 if (i > 1000) 31  { 32                     Thread.Sleep(500); 33                     break; 34  } 35  } 36             return 1; 37         }
View Code

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

相关文章
相关标签/搜索