原文連接 http://anshien.blog.163.com/blog/static/169966308201082441114173/ 數組
你們都瞭解到ArcGIS中處理大數據量時速度是至關的慢,這時若是你的程序是單線程的,那可就讓人着急壞了,不知道處理到什麼地步,不能操做其餘的功能,無奈~~若是在這時你可以想到用多線程技術,那就來試試該如何完成吧。安全
首先,你得有點VS的多線程經驗或學習經驗,得知道什麼多線程,代理(Delegate)是什麼,同步與異步又是什麼,等等。這些在VS的幫助文檔中都有詳細解釋,在這裏我就不越俎代庖了。咱們其中精神去理解ArcGIS中多線程吧。網絡
在ArcgIS中,咱們分幾個部分闡述多線程。數據結構
一、什麼時候使用多線程多線程
在建立多線程應用程序是應注意兩點:線程的安全性和線程的伸縮性。線程安全對於全部的對象都是很是重要的,可是僅僅只有線程安全的對象並不意味着成功建立多線程應用程序,或者說線程安全可以提升應用程序的性能。併發
.NET框架容許你在應用程序中可以迅速的建立線程,可是,在編寫ArcObjects代碼的多線程必需要當心。ArcObjects最根本的結構是組件對象模型(COM)。從這一點來講,編寫ArcObjects的多線程的代碼須要既瞭解.NET多線程,又要了解COM多線程模型。框架
多線程並不老是使你的程序跑的很快,在許多狀況下,它還會增長開支和複雜性,這些最終會減慢程序的執行速度。當增長的複雜性是值得的,那麼多線程才能使用。一個基本的原則是,若是一個任務能夠分解爲不一樣的獨立任務時,那這個任務是適合多線程的。異步
二、ArcObjects線程模型ide
全部的ArcObjects組件都被標記爲單線程單元(STA參考VS幫助文檔)。每一個STA都限制在一個線程中,可是COM並不限制每一個進程中STA的數目。當一個方法調用進入一個STA,它被轉移到STA的惟一線程。所以,在STA中的一個對象將一次只接收和處理一個方法調用,它接收的每一個方法調用會到達同一線程。工具
ArcObjects組件是線程安全的,開發者可把他們在多線程環境下使用。對於AO應用程序在多線程環境下有效運行,由AO所使用的線程單元模型,即獨立線程,必須加以考慮。該模型的工做原理是消除跨線程通訊。一個線程內全部ArcObjects對象的引用應當只與在同一個線程的對象進行通訊。
對於此模型的運行,在ArcGIS 9.X中單個對象都被設計爲線程惟一,而非進程惟一。在進程中管理多個對象的資源消耗超過由制止跨線程通訊所得到的性能提高幅度。
對於擴展ArcGIS系統的開發者,全部對象甚至包括你創造的對象都必須遵循這一規則,孤立線程工做。若是你建立的對象作爲開發的一部分,你必須確保它們是線程惟一,而不是進程惟一。線程惟一就是防止跨線程通訊,這裏ArcGIS Engine中多線程的首要規則。
三、多線程方案
儘管有不少實現多線程應用程序的方式,但如下幾種方案是開發者常用的方式。
3.一、後臺線程執行長事務
當要求須要長事務進行工做時,在後臺執行長事務是可取的,而且同時讓應用程序靈活的操做其餘任務,並讓界面處於響應狀態。這一操做的例子不少,如:使用FeatureCursor來重複向DataTable裝載數據,進行復雜的拓撲計算並寫入新的FeatureClass。爲了完成這類任務,請記住如下幾點:
a. 根據在孤立模型中的線程,你不能在線程之間共享ArcObjects的組件。相反,你須要考慮的是,單個對象都在各自線程中,並在後臺線程中,例如全部工廠須要打開FeatureClass,創造新的FeatureClass,設置空間參考等等。
b.傳遞給線程的全部信息必須是簡單類型或託管類型的形式。
c.萬一在某種狀況下,你要從主線程向工做線程傳遞ArcObjects組件,能夠將對象序列化成字符串,再將字符串傳遞給目標線程,而後再反序列化還原到對象。例如,你可使用XmlSerializerClass序列化對象成爲字符串,如工做區間(Workspace)鏈接屬性(IPropertySet),再將這一字符串傳遞給目標線程,而後在工做線程中使用XmlSerializerClass反序列化鏈接屬性。這樣,就將鏈接屬性對象在後臺再次創造出來,從而避免了跨線程訪問。
當運行後臺線程,你可以在用戶界面瞭解任務的進度。
3.二、實施單機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.三、使用託管線程池和BackgroundWorker的線程
線程池線程都是後臺線程。線程池經過提供一個由系統管理的應用程序線程池使你使用線程更有效率。利用爲每一個任務建立一個新線程的線程池的優勢是線程建立和銷燬的開銷是可忽略的,它能夠帶來更好的性能和更好的系統穩定性。
然而,設計的全部ThreadPool線程是在多線程單元(MTA),所以不該該被用來運行ArcObjects,它們是單線程單元。若要解決此問題,您有幾種選擇。一個是實現一個專用ArcObjects的線程,它被標記爲STAThread並委派每一個從MTA線程調用這個專用ArcObjects線程。另外一種解決方案是使用自定義的STA線程池的實現,如標記爲STA線程的線程數組來運行 ArcObjects。
3.四、同步運行線程的併發執行
在許多狀況下,您必須同步執行的併發運行的線程。一般,你要等待一個或多個線程完成他們的任務,當必定條件下獲得知足,一個等待線程的信號恢復其任務,條件如:測試是給定線否程激活和運行,改變線程優先級,或給予其餘一些條件。
在.NET中有幾種方法來管理運行線程的執行。可用來幫助線程管理的主要幾類以下:
System.Threading.Thread;
System.Threading.WaitHandle;
System.Threading.Monitor;
System.Threading.AutoResetEvent and System.Threading.ManualResetEvent。
3.五、在多個線程共享一個託管類型
有時候你的.NET應用程序的底層數據結構將是一個如DataTable或哈希表管理的對象。這些.NET託管對象容許你在多個線程共享數據獲取,如線程和主線程渲染他們。可是,您應該諮詢MSDN Web站點以驗證這一點是不是線程安全的。在許多狀況下,一個對象是線程讀安全,而寫並不安全。有些集合實施同步方法,它提供了一個底層集合的同步包裝。
在你的對象被多個線程訪問的狀況下,根據MSDN關於這種狀況的對象線程安全規則,你應該得到一個獨佔鎖。取得這樣的獨佔鎖可以完成上面所描述的同步方法,或使用lock語句,它經過獲取給定對象的相互排他鎖標籤一個關鍵塊。它能夠確保,若是另外一個線程試圖訪問對象時,它會被阻塞,直到該對象被釋放(退出鎖)。
3.六、從後臺線程更新用戶界面
在大多數狀況下,您正在使用一個後臺線程來執行長時間的操做,你想向用戶報告進度,狀態,錯誤或其餘與該線程執行的任務相關的信息。這能夠經過更新一個應用程序的用戶界面控件來實現。可是,在Windows中,窗體控件綁定到一個特定的線程(一般是主線程),而且不是線程安全的。所以,你必須委派,從而結合,任何調用UI控件的線程來控制它的所屬。該委託是經過調用Control.Invoke方法,該方法在線程上執行委託,該委託擁有控件的基礎窗口句柄。要驗證調用者是否必須調用Invoke方法,你可使用屬性Control.InvokeRequired。您必須確保該控件的句柄再嘗試調用Control.Invoke或Control.InvokeRequired以前已經建立。
3.七、從一個線程調用ArcObjects而不是主線程
在許多多線程應用程序中,你將須要從不一樣線程調用AO組件。例如,你可能有一個後臺線程來獲取Web服務,這反過來,應該增長新的項目到地圖顯示,響應更改地圖,或運行的geoprocessing(gp)的工具來執行某些類型分析。
一個很是常見的狀況是從一個計時器事件處理方法調用ArcObjects。計時器的Elapsed事件是在一個線程池的任務提出,這不是一個主線程。然而,它須要使用ArcObjects,這好像是須要跨單元調用。然而,這能夠避免處理ArcObjects的組件,就好像AO組件是一個用戶界面控件和使用Invoke來調用委派到建立ArcObjects組件的主線程中。所以,沒有跨單元調用。
ISynchronizeInvoke接口包括的方法有Invoke,BeginInvoke,和EndInvoke。本身實現這些方法多是一個艱鉅的任務。相反,你應該有你直接從System.Windows.Forms.Control繼承的類或者你應該有一個助手類,它繼承自控件。要麼選擇將提供一個簡單而有效的對於調用方法的解決方案。
1 private void button5_Click(object sender, EventArgs e) 2 { 3 Func(); 4 } 5 private delegate int SomeDelegate(Array array); 6
7 void Func() 8 { 9 SomeDelegate del = new SomeDelegate(AnotherFunc);//AnotherFunc與SomeDelegate一樣的形式
10
11 IAsyncResult ireslt = del.BeginInvoke(null, null, null);//異步操做
12 ProgressbarForm form = new ProgressbarForm();//異步操做中的進度條窗體
13 form.setProgressBar("提示", "正在處理數據...", 10, 0, 100); 14 form.Show(); 15 System.Windows.Forms.Application.DoEvents(); 16 while (!ireslt.IsCompleted) 17 { 18 System.Windows.Forms.Application.DoEvents(); 19 } 20 int something = del.EndInvoke(ireslt); 21 form.Close(); 22 } 23 private int AnotherFunc(Array array) 24 { 25 int i = 0; 26 while (true) 27 { 28 i++; 29 Thread.Sleep(10); 30 if (i > 1000) 31 { 32 Thread.Sleep(500); 33 break; 34 } 35 } 36 return 1; 37 }
以上是理論方面的闡述及一個本人開發過程當中的一個代碼片斷,但願這些可以幫助你完成你的多線程程序。參考的資料以下:Windows MSDN,ESRI 的開發者網站。