首页 > 其他分享 >关于ArcEngine在多线程模式下的注意点

关于ArcEngine在多线程模式下的注意点

时间:2024-01-15 14:55:47浏览次数:26  
标签:AE 模式 问题 GDB 线程 多线程 COM ArcEngine

仅以我的环境来描述的我问题和解决方案,超出该范围的暂时没有考虑。

一、环境

ArcEngine 10.2

语言:C#

.net版本:4.6.1

二、需求

创建GDB数据库,并从json文件把数据写入GDB中,包含了图形数据,为了兼顾效率,我使用了多线程来生成GDB,但也做了控制,一个线程只会对一个GDB进行操作。

三、问题:

无法将类型为"Systerm._ComObject"的对象强制转换为类型FileGDBWorkspaceFactory();

四、问题分析

这个问题困扰我很久,在网上找过好几个解决方案,但都不适用,因为报的是COM组件的问题,当时一直认为是AE不支持多线程操作导致,于是做了一个处理,就是在创建或读取GDB的时候,加了个循环,出错了就隔10秒再读,一共5次重试。

这个方案一定程度上缓解了问题,但并没有解决,因为连续5次都以上错的概率,还是挺高的。

当时我其实也是把问题推给了AE,就跟实施说,这个是AE的问题,解决不了,除非换单线程,但是换单线程导出的速度又不行,所以在这里纠结了很长时间。

直到上周五吧,我突然留意到一个问题,报错的位置,并不是在打开或者创建GDB那一行,而是在实例化工厂那一行,也就是下面这行:

IWorkspaceFactory2 wsFctry = new FileGDBWorkspaceFactoryClass();

我突然觉得不对劲,这里还没到具体的操作GDB呢,别问我为什么现在才发现报错在这行,因为我一直都觉得是AE的问题,我一直都认为AE在多线程模式下是有问题的,出错是理所当然的,只能绕过去,所以我压根没有细细去想,去分析。唉。。多么痛的领悟。

后来查找到一个解决方案,就是使用反射的方式创建工厂,如下:

Type t = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
System.Object obj = Activator.CreateInstance(t);
var gdbWorkspaceFactory = obj as IWorkspaceFactory;

OK,到这里,创建工厂的问题确实解决了,后面没有再报这个错,可是!!问题又来了

报错:COM组件异常来自 HRESULT:0x80010105 (RPC_E_SERVERFAULT)的错误,所以,我考虑,仍然是多线程的问题,另外,目前这种创建方式,每操作一次GDB,就创建一个工厂,用来操作GDB,这可能会导致同时创建了大量的工厂,没这个必要。于是,根据目前的情况,改造点有两个:

1、避免创建过多厂,只创建一个即可

2、操作帮助类的时候,将帮助类封闭在一个线程,不出现任何跨线程变量共享和情况。

经改造,关键代码如下:

private IWorkspaceFactory gdbWorkspaceFactory = null;

 /// <summary>
        /// 读取GDB工作空间
        /// </summary>
        /// <param name="FileGDBPath">FileGDBPath</param>
        /// <returns>IWorkspace</returns>
        public IWorkspace OpenFileGDBWorkspace(string FileGDBPath)
        {
            lock (locker)
            {
                MakeFactory();
                FileGDBPath = System.IO.Path.GetFullPath(FileGDBPath);
                return this.gdbWorkspaceFactory.OpenFromFile(FileGDBPath, 0);
            }
        }
 private void MakeFactory()
        {

            if (this.gdbWorkspaceFactory == null)
            {
                Type t = Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory");
                System.Object obj = Activator.CreateInstance(t);
                this.gdbWorkspaceFactory = obj as IWorkspaceFactory;
            }
        }

在每个线程中,独立实例化一个类来操作GDB:

Task.run(()=>{
    Export();
});
void Export(){
GdbHelper gdbHelper = new GdbHelper();
gdbHelper.OpenFileGDBWorkspace(path);
}

以上只是演示代码,不是我的实际代码。

经过一晚上的压测,将COM操作封闭在一个线程中,保证一个线程中只有一 个工厂实例后,没有再报错,至此,暂时证明了这个问题可以用这个方法解决。

五、总结

这个问题之所以这么久才解决,主要原因还是因为自己对AE不太熟悉,遇到问题,没有认真去仔细分析,而是把问题推掉。如果一开始就静下心来去分析问题,可能早就解决了。

这个问题其实确实是多线程导致,在多线程环境中使用 COM 组件时需要注意线程安全性和 COM 组件安全性。COM 组件在设计时可能是单线程单元(STA - Single-Threaded Apartment)的,这意味着它们在单个线程上运行,而不是设计为在多线程环境中并发使用。

在 ArcObjects 中,许多组件是 STA 的,包括 FileGDBWorkspaceFactoryClass。为了确保线程安全性,应该将操作封闭在一个线程中,避免跨线程实例同一个实例。

标签:AE,模式,问题,GDB,线程,多线程,COM,ArcEngine
From: https://www.cnblogs.com/lythen/p/17965360

相关文章

  • 访问模式(visitor)
    1#include<iostream>2#include<string>3usingnamespacestd;45//访问者模式:核心叫做双重分发:两个多态:accept,visit67classXiaoMi;8classHuaWei;9classOppo;10classCellPhoneVisitor{11public:12virtualvoidvisit(Xia......
  • python多线程模块:threading使用方法(参数传递)
    先来看这段代码:importthreadingimporttimedefworker():print“worker”time.sleep(1)returnforiinxrange(5):t=threading.Thread(target=worker)t.start()这段代码就使用了多线程,但是没法传递参数,而实际使用多线程,往往是需要传递参数的......
  • 企业如何选择适合自己的即时通讯软件?先了解这3种部署模式
    当今信息化时代,即时通讯软件已经成为企业日常沟通的不可或缺的工具,而如何选择适合自己的即时通讯软件也成为了企业面临的重要问题。下面我将从部署方式出发,深入分析即时通讯软件的部署方式以及优劣势,帮助企业更好地选择适合自己的即时通讯软件。一、Saas部署模式SaaS部署模式是指基......
  • CompletableFuture多线程与redis分布式锁
    @AutowiredpublicRedisTemplateredisTemplate;booleanlock=redisTemplate.opsForValue().setIfAbsent("lock","redisLock");//获取锁      booleanredisLock=redis.getRedisLock();      if(redisLock){        //创建线......
  • 设计模式--UML类图
    ‘动物’矩形框,它就代表一个类(Class)。类图分三层,第一层显示类的名称,如果是抽象类,则就用斜体显示。第二层是类的特性,通常就是字段和属性。第三层是类的操作,通常是方法或行为。注意前面的符号,‘+’表示public,‘-’表示private,‘#’表示protected......
  • 【设计模式】策略模式——策略模式在JDK源码中的应用
    策略模式在JDK中具有广泛的应用,本文只讨论最常见的应用。RejectedExecutionHandler在线程池使用有界队列并且最大线程数不为Integer.MAX_VALUE的时候,一旦task数量达到临界点,新的task添加到线程池的时候就会出现问题,ThreadPoolExecutor的构造方法中参数最多的方法中最后一个参数就是......
  • python面向对象之单例模式的使用
    单例模式​ 单例模式(SingletonPattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。​ 比如,某个程序的配置信息存放在一个文件中,客户端通过一个Appconfig的类来读取配置......
  • spring与设计模式之四适配器模式
    一、定义适配器模式-或者称为转接口模式,变压器模式。通过适配,可以让原来提供特定功能的对象完成另外一个标准的功能。所以,所谓的适配应该可以这样称呼:让某些类/接口适配/转换某个标准/功能。适配器器的重点是适配,就是新增(装饰)。为了便于记忆和理解,读者最好根据自己的习惯来命......
  • 多线程
    01-程序、进程与线程1.程序、进程和线程的区分:程序(program):为完成特定任务,用某种语言编写的`一组指令的集合`。即指一段静态的代码。进程(process):程序的一次执行过程,或是正在内存中运行的应用程序。程序是静态的,进程是动态的。进程作为操作系统调度和分配......
  • 建造者模式
    定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,用户只需要指定需要建造的类型就可以得到它们,建造过程及细节不需要知道使用场景:如果一个对象有非常复杂的数据结构(很多属性),想把复杂的创建和使用分离优点:封装性好,创建和使用分离扩展性......