一直都想寫寫AE中多執行緒的使用,但一直苦於沒有時間,終於在中秋假期閒了下來。呵呵,閒話不說了,進入正題!
大家都瞭解到ArcGIS中處理大資料量時速度是相當的慢,這時如果你的程式是單執行緒的,那可就讓人著急壞了,不知道處理到什麼地步,不能操作其他的功能,無奈~~如果在這時你能夠想到用多執行緒技術,那就來試試該如何完成吧。
首先,你得有點VS的多執行緒經驗或學習經驗,得知道什麼多執行緒,代理(Delegate)是什麼,同步與非同步又是什麼,等等。這些在VS的幫助文件中都有詳細解釋,在這裡我就不越俎代庖了。我們其中精神去理解ArcGIS中多執行緒吧。
在ArcgIS中,我們分幾個部分闡述多執行緒。
1、何時使用多執行緒
在建立多執行緒應用程式是應注意兩點:執行緒的安全性和執行緒的伸縮性。執行緒安全對於所有的物件都是非常重要的,但是僅僅只有執行緒安全的物件並不意味著成功建立多執行緒應用程式,或者說執行緒安全能夠提高應用程式的效能。
.NET框架允許你在應用程式中能夠迅速的建立執行緒,但是,在編寫ArcObjects程式碼的多執行緒必須要小心。ArcObjects最根本的結構是元件物件模型(COM)。從這一點來說,編寫ArcObjects的多執行緒的程式碼需要既瞭解.NET多執行緒,又要了解COM多執行緒模型。
多執行緒並不總是使你的程式跑的很快,在許多情況下,它還會增加開支和複雜性,這些最終會減慢程式的執行速度。當增加的複雜性是值得的,那麼多執行緒才能使用。一個基本的原則是,如果一個任務可以分解為不同的獨立任務時,那這個任務是適合多執行緒的。
2、ArcObjects執行緒模型
所有的ArcObjects元件都被標記為單執行緒單元(STA參考VS幫助文件)。每個STA都限制在一個執行緒中,但是COM並不限制每個程式中S他的數目。當一個方法呼叫進入一個STA,它被轉移到S他的唯一執行緒。因此,在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
|
delegate SomethingClassType SomeDelegate(IArray array);
SomeDelegate del = new SomeDelegate(AnotherFunc); //AnotherFunc與SomeDelegate同樣的形式
IAsyncResult ireslt = del.BeginInvoke(array, null , null ); //非同步操作
ProgressbarForm form = new ProgressbarForm(); //非同步操作中的進度條窗體
form.Show(); System.Windows.Forms.Application.DoEvents(); while (!ireslt.IsCompleted)
{ System.Windows.Forms.Application.DoEvents(); } SomethingClassType something= del.EndInvoke(ireslt); form.Close(); |
以上是理論方面的闡述及一個本人開發過程中的一個程式碼片段,希望這些能夠幫助你完成你的多執行緒程式。參考的資料如下:Windows MSDN,ESRI 的開發者網站。