C#“必须先将当前线程设置为单个线程单元(STA)模式方可进行OLE调用”异常解决方案

2019-12-13 17:54栏目:编程
TAG:

  关于这类问题网上搜索会有很多解决方案,但基本的意思都相差不大,大致问题出于启用线程时调用类似剪贴板Clipboard.SetDataObject出错,我把我的测试代码展现下:

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

图片 1

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

  解决方案:只需将thread.SetApartmentState(ApartmentState.STA);反注释就可以了。

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

  出现的原因:

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

*  单元是进程内部具有相同线程访问要求的对象的逻辑容器。同一单元中的所有对象都可以接收从该单元中的任何线程发出的调用。
.NET Framework 不使用单元,托管对象自己负责以线程安全的方式使用所有共享资源。由于 COM 类使用单元,因此公共语言运行
库需要在 COM interop 的情况下调用 COM 对象时创建并初始化一个单元。托管线程可以创建并进入只允许有一个线程的单线程单
元 (STA) 或者包含一个或多个线程的多线程单元 (MTA)。通过将线程的 ApartmentState 属性设置为 ApartmentState 枚举值之一
,可以控制所创建的单元的类型。由于给定线程只能初始化 COM 单元一次,因此在第一次调用非托管代码之后就不能更改单元类型。*

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

注:在网络上找了两篇文章或许更能说明这个问题。文章的大意是,由于很多COM在.NET环境下如果使用多线程的话,会导致引用的COM不能正常运行,
而如果不声明程序为STAThread的话,.NET就会自动使用多线程来提高效率,这样就会导致不可预知的后果。

       1、何时使用多线程

  A young ilder ~ an old beggar !

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

.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]。

版权声明:本文由bob体育app发布于编程,转载请注明出处:C#“必须先将当前线程设置为单个线程单元(STA)模式方可进行OLE调用”异常解决方案