天天看點

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繼承的類或者你應該有一個助手類,它繼承自控件。要麼選擇将提供一個簡單而有效的對于調用方法的解決方案。

1

2

3

4

5

6

7

8

9

10

11

12

<code>delegate</code> <code>SomethingClassType SomeDelegate(IArray array);</code>

<code>SomeDelegate del = </code><code>new</code> <code>SomeDelegate(AnotherFunc);</code><code>//AnotherFunc與SomeDelegate同樣的形式</code>

<code>IAsyncResult ireslt = del.BeginInvoke(array, </code><code>null</code><code>, </code><code>null</code><code>);</code><code>//異步操作</code>

<code>ProgressbarForm form = </code><code>new</code> <code>ProgressbarForm();</code><code>//異步操作中的進度條窗體</code>

<code>form.Show();</code>

<code>System.Windows.Forms.Application.DoEvents();</code>

<code>while</code> <code>(!ireslt.IsCompleted)</code>

<code>{</code>

<code>}</code>

<code>SomethingClassType something= del.EndInvoke(ireslt);</code>

<code>form.Close();</code>

 以上是理論方面的闡述及一個本人開發過程中的一個代碼片段,希望這些能夠幫助你完成你的多線程程式。參考的資料如下:Windows MSDN,ESRI 的開發者網站。

沒有整理與歸納的知識,一文不值!高度概括與梳理的知識,才是自己真正的知識與技能。 永遠不要讓自己的自由、好奇、充滿創造力的想法被現實的架構所束縛,讓創造力自由成長吧! 多花時間,關心他(她)人,正如别人所關心你的。理想的騰飛與實作,沒有别人的支援與幫助,是萬萬不能的。

    本文轉自wenglabs部落格園部落格,原文連結:http://www.cnblogs.com/arxive/p/8145403.html,如需轉載請自行聯系原作者