基于C#的ArcEngine二次开发37:循环查询过程的内存管理与性能优化(一)

简介: 基于C#的ArcEngine二次开发37:循环查询过程的内存管理与性能优化

注:文章第四部分搬运自ArcEngine空间查询优化;版权归原作者所有,特此声明

1 查询接口函数解析

1.1 IFeatureClass.Search()

1.1.1 函数说明

[C#]public IFeatureCursor Search ( IQueryFilterfilter, boolRecycling);

Search将返回一个满足IQueryFilter 设定的属性查询或空间查询条件的IFeatureCursor 对象;如果该参数为null,则返回要素类中的所有要素。参数recycling 控制行对象的分配行为,循环光标在提取单个要素目标都会重新进行初始化,并采用只读优化;在多次调用游标的NextFeature时,保持对要素的引用是非法的;通过回收游标返回的要素是不可修改的,不可回收游标在每个对象提取时,返回的是单独的对象,可以被多种行为的修改和存储。


The recycling parameter controls row object allocation behavior. Recycling cursors rehydrate a single feature object on each fetch and can be used to optimize read-only access, for example, when drawing. It is illegal to maintain a reference on a feature object returned by a recycling cursor across multiple calls to NextFeature on the cursor. Features returned by a recycling cursor should not be modified. Non-recycling cursors return a separate feature object on each fetch. The features returned by a non-recycling cursor may be modified and stored with polymorphic behavior.


The Geodatabase guarantees "unique instance semantics" on non-recycling feature objects fetched during an edit session. In other words, if the feature retrieved by a search cursor has already been instantiated and is being referenced by the calling application, then a reference to the existing feature object is returned.


Non-recycling feature cursors returned from the Search method *MUST* be used when copying features from the cursor into an insert cursor of another class.  This is because a recycling cursor reuses the same geometry and under some circumstances all of the features inserted into the insert cursor may have the same geometry.  Using a non-recycling cursor ensures that each geometry is unique.


1.1.2 依据点坐标查找图层中的第一个要素


The code in this document requires the following References added to the Visual Studio project:

ESRI.ArcGIS.Carto

ESRI.ArcGIS.Geodatabase

ESRI.ArcGIS.Geometry

ESRI.ArcGIS.System

///<summary>Finds the first feature in a GeoFeature layer by supplying an point.  The point could come from a mouse click in the map.</summary>
///
///<param name="searchTolerance">A System.Double that is the number of map units to search. Example: 25</param>
///<param name="point">An IPoint interface in map units where the user clicked on the map</param>
///<param name="geoFeatureLayer">An ILayer interface to search upon</param>
///<param name="activeView">An IActiveView interface</param>
/// 
///<returns>An IFeature interface that is the first feature found in the GeoFeatureLayer.</returns>
/// 
///<remarks></remarks>
public ESRI.ArcGIS.Geodatabase.IFeature GetFirstFeatureFromPointSearchInGeoFeatureLayer(
 System.Double searchTolerance,
 ESRI.ArcGIS.Geometry.IPoint point,
 ESRI.ArcGIS.Carto.IGeoFeatureLayer geoFeatureLayer,
 ESRI.ArcGIS.Carto.IActiveView activeView)
{
  if (searchTolerance < 0 || point == null || geoFeatureLayer == null || activeView == null)
  {
    return null;
  }
  ESRI.ArcGIS.Carto.IMap map = activeView.FocusMap; 
  // Expand the points envelope to give better search results    
  ESRI.ArcGIS.Geometry.IEnvelope envelope = point.Envelope;
  envelope.Expand(searchTolerance, searchTolerance, false);
  ESRI.ArcGIS.Geodatabase.IFeatureClass featureClass = geoFeatureLayer.FeatureClass;
  System.String shapeFieldName = featureClass.ShapeFieldName;
  // Create a new spatial filter and use the new envelope as the geometry    
  ESRI.ArcGIS.Geodatabase.ISpatialFilter spatialFilter = new ESRI.ArcGIS.Geodatabase.SpatialFilterClass();
  spatialFilter.Geometry = envelope;
  spatialFilter.SpatialRel = ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects;
  spatialFilter.set_OutputSpatialReference(shapeFieldName, map.SpatialReference);
  spatialFilter.GeometryField = shapeFieldName;
  // Do the search
  ESRI.ArcGIS.Geodatabase.IFeatureCursor featureCursor = featureClass.Search(spatialFilter, false);
  // Get the first feature
  ESRI.ArcGIS.Geodatabase.IFeature feature = featureCursor.NextFeature();
  if (!(feature == null))
  {
    return feature;
  }
  else
  {
      return null;
  }
}

1.2 IFeatureClass.Update()

1.2.1 函数说明

[C#]public IFeatureCursor Update ( IQueryFilterfilter, boolRecycling);


配合更新游标使用,通过过滤器filter进行属性或空间查询,如果选择一组特定的查询要素对其进行更新,则使用更新游标会比逐个要素更新更有效率,其更新是通过当前游标的位置执行。更新游标可作为带有多态行为(polymorphic behavior)要素子类的实例。更新游标即可在编辑会话一起使用,也可以单独使用;当在编辑会话内部使用更新游标时,这些变化知道编辑会话被保存才会提交到基础表中;网络要素类、拓扑要素类、参与组合关系或其他信息关系的要素类,只能通过编辑会话更新。如果你尝试在编辑会话之外使用更新游标,它将会失败;除此之外,编辑参与拓扑或几何网络的的要素必须要在编辑会话内。当在编辑会话中使用游标时,必须将其作用域设置为编辑操作;也就是说,游标可在编辑操作开始之后创建,且在编辑操作停止或中止之后不应再被使用。


Update opens an update cursor on the features specified by an attribute and/or spatial query as specified by the filter parameter. If a number of features selected by a particular query are to be updated and each feature is to be updated to a separate value then the update cursor is faster than doing an individual feature level update for each feature. The update is performed on the current 'cursor position'.Update cursors can be used on instances of Feature subclasses (such as network features), with guaranteed polymorphic behavior. Update cursors can be used either inside or outside of an edit session. If used inside an edit session, the changes are not committed to the base table until the edit session is saved. Network feature classes, Topology feature classes, feature classes that participate in composite relationships or other relationships with messaging may only be updated within an edit session. If you attempt to use an update cursor on one of these classes outside of an edit session, it will fail.  In addition, edits to features that participate in a Topology or Geometric Network must be bracketed within an edit operation.When using cursors within an edit session, they should always be scoped to edit operations. In other words, a cursor should be created after an edit operation has begun and should not be used once that edit operation has been stopped or aborted.

1.2.2 游标要素更新

public void UseUpdateCursor(IFeatureClass featureClass)
{
    // Restrict the number of features to be updated.
    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.WhereClause = "NAME = 'Highway 104'";
    queryFilter.SubFields = "TYPE";
    // Use IFeatureClass.Update to populate IFeatureCursor.
    IFeatureCursor updateCursor = featureClass.Update(queryFilter, false);
    int typeFieldIndex = featureClass.FindField("TYPE");
    IFeature feature = null;
    try
    {
        while ((feature = updateCursor.NextFeature()) != null)
        {
            feature.set_Value(typeFieldIndex, "Toll Highway");
            updateCursor.UpdateFeature(feature);
        }
    }
    catch (COMException comExc)
    {
        // Handle any errors that might occur on NextFeature().
    }
    // If the cursor is no longer needed, release it.
    Marshal.ReleaseComObject(updateCursor);
}

1.3 IFeatureClass.Insert()

1.3.1 函数说明

[C#]public IFeatureCursor Insert (bool useBuffering);


插入要素

1.3.2 示例

示例1:使用插入游标插入要素


public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List <
    IGeometry > geometryList)
{
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Create a feature buffer.
        IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
        comReleaser.ManageLifetime(featureBuffer);
        // Create an insert cursor.
        IFeatureCursor insertCursor = featureClass.Insert(true);
        comReleaser.ManageLifetime(insertCursor);
        // All of the features to be created are classified as Primary Highways.
        int typeFieldIndex = featureClass.FindField("TYPE");
        featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
        foreach (IGeometry geometry in geometryList)
        {
            // Set the feature buffer's shape and insert it.
            featureBuffer.Shape = geometry;
            insertCursor.InsertFeature(featureBuffer);
        }
        // Flush the buffer to the geodatabase.
        insertCursor.Flush();
    }
}

示例2:使用仅加载模式插入要素

public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry >
    geometryList)
{
    // Cast the feature class to the IFeatureClassLoad interface.
    IFeatureClassLoad featureClassLoad = (IFeatureClassLoad)featureClass;
    // Acquire an exclusive schema lock for the class.
    ISchemaLock schemaLock = (ISchemaLock)featureClass;
    try
    {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
        // Enable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = true;
        using(ComReleaser comReleaser = new ComReleaser())
        {
            // Create the feature buffer.
            IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
            comReleaser.ManageLifetime(featureBuffer);
            // Create an insert cursor.
            IFeatureCursor insertCursor = featureClass.Insert(true);
            comReleaser.ManageLifetime(insertCursor);
            // All of the features to be created are classified as Primary Highways.
            int typeFieldIndex = featureClass.FindField("TYPE");
            featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
            foreach (IGeometry geometry in geometryList)
            {
                // Set the feature buffer's shape and insert it.
                featureBuffer.Shape = geometry;
                insertCursor.InsertFeature(featureBuffer);
            }
            // Flush the buffer to the geodatabase.
            insertCursor.Flush();
        }
    }
    catch (Exception)
    {
        // Handle the failure in a way appropriate to the application.
    }
    finally
    {
        // Disable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = false;
        // Demote the exclusive schema lock to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}

2 资源的释放

在进行数据查询时,如果不对游标进行释放,查询效率会越来越慢

2.1 使用Marsh.ReleaseComObject进行查询游标的释放

        private void find_allroads_lxbm(IFeature feature_ld, string lxbm)
        {
            try
            {
                ISpatialFilter spatialfilter = new SpatialFilterClass();
                spatialfilter.WhereClause = "[LXBM] = " + lxbm;
                spatialfilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
                //feature_ld该路段 feature_last下一路段
                while (feature_ld != null)
                {
                    IPolyline polyline = feature_ld.Shape as IPolyline;
                    //缓冲
                    IPoint end_pt = polyline.ToPoint;
                    ITopologicalOperator topo2 = end_pt as ITopologicalOperator;
                    IGeometry geo_buffer_end = topo2.Buffer(1);
                    spatialfilter.Geometry = geo_buffer_end;
                    IFeatureCursor featcursor_lastld = m_featcls.Search(spatialfilter, true);
                    IFeature feature_last = featcursor_lastld.NextFeature();
                    try
                    {
                        while (feature_last != null)
                        {                         
                           /****省略***/                                                 
                        }
                    }
                    catch (Exception exx)
                    {                        
                        MessageBox.Show(exx.ToString());
                    }                 
                    //下一个
                    feature_ld = feature_last;
             System.Runtime.InteropServices.Marshal.ReleaseComObject(featcursor_lastld);
                }
                System.Runtime.InteropServices.Marshal.ReleaseComObject(spatialfilter);
                垃圾回收
                System.GC.Collect();
                System.GC.WaitForPendingFinalizers();
            }
            catch (Exception exx)
            {
                MessageBox.Show(exx.ToString());
            }           
        }  

2.2 分析

情况一:在使用完游标之后,将其赋值为null;这时候对象实际上是没有被释放掉的,因此 可能会报 '超出打开游标最大数’;


情况二:在使用完游标时,在循环末尾,用Marshal.ReleaseComObject方法来释放com对象,同时在使用Marshal.ReleaseComObject 方法时,并没有增加多余的时间,循环执行时间上还是跟不释放对象一样(小数据量比较)。


结论:


在使用ArcEngine中的游标对象时,一定要在使用完之后进行对象的释放,否则会不定时出现上面的错误;而且需要使用marshal.releasecomobject方法来进行对象的释放,赋值为null是达不到目的的


Search()版本

IFeatureCursor pFeatureCursor = featureClass.Search(null, true);
IFeature pFeature = pFeatureCursor.NextFeature();
while(pFeature != null)
{
    //自已的一通操作
    pFeature.Store();//保存
    pFeature = pFeatureCursor.NextFeature();
}
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

Update()版本

IFeatureCursor pFeatureCursor = featureClass.Update(null, true);
IFeature pFeature = pFeatureCursor.NextFeature();
while(pFeature != null)
{
    //自已的一通操作
    pFeature.UpdateFeature(pFeature );//保存
    pFeature = pFeatureCursor.NextFeature();
}
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

Insert()版本

IFeatureCursor pFeatureCursor = featureClass.Insert(true);
IFeature pFeature = pFeatureCursor.NextFeature();
IFeatureBuffer pFeatureBuffer = featureClass.CreatFeatureBuffer();
while(pFeature != null)
{
    //对pFeatureBuffer执行一波操作
    pFeatureCursor.UpdateFeature(pFeatureBuffer);//保存
    pFeature = pFeatureCursor.NextFeature();
}
pFeatureCursor.Flush();
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

建议就是尽量避免对数据执行大量的循环查询操作,如果能采用内存优化的,就直接使用内存,这样可以快速提升程序的运行效率;但是对于,海量数据,迫不得已必须进行大量的循环操作时,请注意及时释放游标,释放内存资源,以确保程序性能。


相关文章
|
5月前
|
Oracle 关系型数据库 Linux
解决在linux服务器上部署定时自动查找cpu,内存,磁盘使用量,并将查询结果写入数据库的脚本,只能手动运行实现插库操作
问题描述:将脚本名命名为mortior.sh(以下简称mo),手动执行脚本后查询数据库,表中有相应的信息,放入自动执行队列中,脚本被执行,但是查询数据库,并没有新增数据。
44 0
|
6月前
|
C#
CAD2015 C#二次开发 字体变形
CAD2015 C#二次开发 字体变形
|
3月前
|
存储 缓存 Java
Java性能优化: 如何减少Java程序的内存占用?
Java性能优化: 如何减少Java程序的内存占用?
245 2
|
1月前
|
存储 缓存 监控
Linux 系统 内存通用指标以及查询方式
Linux 系统 内存通用指标以及查询方式
18 0
|
1月前
|
监控 Java Android开发
构建高效Android应用:从内存管理到性能优化
【2月更文挑战第30天】 在移动开发领域,打造一个流畅且响应迅速的Android应用是每个开发者追求的目标。本文将深入探讨如何通过有效的内存管理和细致的性能调优来提升应用效率。我们将从分析内存泄露的根本原因出发,讨论垃圾回收机制,并探索多种内存优化策略。接着,文中将介绍多线程编程的最佳实践和UI渲染的关键技巧。最后,我们将通过一系列实用的性能测试工具和方法,帮助开发者监控、定位并解决性能瓶颈。这些技术的综合运用,将指导读者构建出更快速、更稳定、用户体验更佳的Android应用。
|
3月前
|
SQL 算法 关系型数据库
大查询会不会把内存打爆
大查询会不会把内存打爆
|
3月前
|
SQL 开发框架 .NET
C#进阶-LINQ表达式之GroupBy分组查询
本篇文章我们将演示LINQ扩展包基础语法里的GroupBy分组查询,并实现投影等实际操作中常用的类型转换手法。目前LINQ支持两种语法,我会在每个案例前先用大家熟知的SQL语句表达,再在后面用C#的两种LINQ语法分别实现。LINQ语法第一次接触难免感到陌生,最好的学习方式就是在项目中多去使用,相信会有很多感悟。
59 0
|
4月前
|
运维 Linux
Linux 查询 OS、CPU、内存、硬盘信息
Linux 查询 OS、CPU、内存、硬盘信息
91 0
|
4月前
|
C# 索引
C# | 【完全开源】手机号码归属地查询,一秒内百万次查询
这个开源项目是一个.NET库,可以通过手机号码获取号码归属地信息,包括运营商、国家、省份、城市、邮政编码、区号等信息。 该库加载了一个包含46万条数据的“中国手机号归属地信息”数据集,并实现了高速查询。在我的7年老笔记本上执行一百万次查询耗时不足一秒。
144 0
|
5月前
|
存储 缓存 NoSQL
Linux内存性能优化总结,让你的系统更加高效!(下)
Linux内存性能优化总结,让你的系统更加高效!