ArcGIS Engine 中的多线程使用
轉(zhuǎn)自原文ArcGIS Engine 中的多線程使用
一直都想寫寫AE中多線程的使用,但一直苦于沒(méi)有時(shí)間,終于在中秋假期閑了下來(lái)。呵呵,閑話不說(shuō)了,進(jìn)入正題!
??????? 大家都了解到ArcGIS中處理大數(shù)據(jù)量時(shí)速度是相當(dāng)?shù)穆?#xff0c;這時(shí)如果你的程序是單線程的,那可就讓人著急壞了,不知道處理到什么地步,不能操作其他的功能,無(wú)奈~~如果在這時(shí)你能夠想到用多線程技術(shù),那就來(lái)試試該如何完成吧。
?????? 首先,你得有點(diǎn)VS的多線程經(jīng)驗(yàn)或?qū)W習(xí)經(jīng)驗(yàn),得知道什么多線程,代理(Delegate)是什么,同步與異步又是什么,等等。這些在VS的幫助文檔中都有詳細(xì)解釋,在這里我就不越俎代庖了。我們其中精神去理解ArcGIS中多線程吧。
?????? 在ArcgIS中,我們分幾個(gè)部分闡述多線程。
?????? 1、何時(shí)使用多線程
在創(chuàng)建多線程應(yīng)用程序是應(yīng)注意兩點(diǎn):線程的安全性和線程的伸縮性。線程安全對(duì)于所有的對(duì)象都是非常重要的,但是僅僅只有線程安全的對(duì)象并不意味著成功創(chuàng)建多線程應(yīng)用程序,或者說(shuō)線程安全能夠提高應(yīng)用程序的性能。
.NET框架允許你在應(yīng)用程序中能夠迅速的創(chuàng)建線程,但是,在編寫ArcObjects代碼的多線程必須要小心。ArcObjects最根本的結(jié)構(gòu)是組件對(duì)象模型(COM)。從這一點(diǎn)來(lái)說(shuō),編寫ArcObjects的多線程的代碼需要既了解.NET多線程,又要了解COM多線程模型。
多線程并不總是使你的程序跑的很快,在許多情況下,它還會(huì)增加開支和復(fù)雜性,這些最終會(huì)減慢程序的執(zhí)行速度。當(dāng)增加的復(fù)雜性是值得的,那么多線程才能使用。一個(gè)基本的原則是,如果一個(gè)任務(wù)可以分解為不同的獨(dú)立任務(wù)時(shí),那這個(gè)任務(wù)是適合多線程的。
2、ArcObjects線程模型
所有的ArcObjects組件都被標(biāo)記為單線程單元(STA參考VS幫助文檔)。每個(gè)STA都限制在一個(gè)線程中,但是COM并不限制每個(gè)進(jìn)程中STA的數(shù)目。當(dāng)一個(gè)方法調(diào)用進(jìn)入一個(gè)STA,它被轉(zhuǎn)移到STA的唯一線程。因此,在STA中的一個(gè)對(duì)象將一次只接收和處理一個(gè)方法調(diào)用,它接收的每個(gè)方法調(diào)用會(huì)到達(dá)同一線程。
ArcObjects組件是線程安全的,開發(fā)者可把他們?cè)诙嗑€程環(huán)境下使用。對(duì)于AO應(yīng)用程序在多線程環(huán)境下有效運(yùn)行,由AO所使用的線程單元模型,即獨(dú)立線程,必須加以考慮。該模型的工作原理是消除跨線程通信。一個(gè)線程內(nèi)所有ArcObjects對(duì)象的引用應(yīng)當(dāng)只與在同一個(gè)線程的對(duì)象進(jìn)行通信。
對(duì)于此模型的運(yùn)行,在ArcGIS 9.X中單個(gè)對(duì)象都被設(shè)計(jì)為線程唯一,而非進(jìn)程唯一。在進(jìn)程中管理多個(gè)對(duì)象的資源消耗超過(guò)由制止跨線程通信所獲得的性能提升幅度。
對(duì)于擴(kuò)展ArcGIS系統(tǒng)的開發(fā)者,所有對(duì)象甚至包括你創(chuàng)造的對(duì)象都必須遵循這一規(guī)則,孤立線程工作。如果你創(chuàng)建的對(duì)象做為開發(fā)的一部分,你必須確保它們是線程唯一,而不是進(jìn)程唯一。線程唯一就是防止跨線程通信,這里ArcGIS Engine中多線程的首要規(guī)則。
3、多線程方案
盡管有很多實(shí)現(xiàn)多線程應(yīng)用程序的方式,但以下幾種方案是開發(fā)者經(jīng)常使用的方式。
3.1、后臺(tái)線程執(zhí)行長(zhǎng)事務(wù)
當(dāng)要求需要長(zhǎng)事務(wù)進(jìn)行工作時(shí),在后臺(tái)執(zhí)行長(zhǎng)事務(wù)是可取的,并且同時(shí)讓應(yīng)用程序靈活的操作其他任務(wù),并讓界面處于響應(yīng)狀態(tài)。這一操作的例子很多,如:使用FeatureCursor來(lái)重復(fù)向DataTable裝載數(shù)據(jù),進(jìn)行復(fù)雜的拓?fù)溆?jì)算并寫入新的FeatureClass。為了完成這類任務(wù),請(qǐng)記住以下幾點(diǎn):
a. 根據(jù)在孤立模型中的線程,你不能在線程之間共享ArcObjects的組件。相反,你需要考慮的是,單個(gè)對(duì)象都在各自線程中,并在后臺(tái)線程中,例如所有工廠需要打開FeatureClass,創(chuàng)造新的FeatureClass,設(shè)置空間參考等等。
b.傳遞給線程的所有信息必須是簡(jiǎn)單類型或托管類型的形式。
c.萬(wàn)一在某種情況下,你要從主線程向工作線程傳遞ArcObjects組件,可以將對(duì)象序列化成字符串,再將字符串傳遞給目標(biāo)線程,然后再反序列化還原到對(duì)象。例如,你可以使用XmlSerializerClass序列化對(duì)象成為字符串,如工作區(qū)間(Workspace)連接屬性(IPropertySet),再將這一字符串傳遞給目標(biāo)線程,然后在工作線程中使用XmlSerializerClass反序列化連接屬性。這樣,就將連接屬性對(duì)象在后臺(tái)再次創(chuàng)造出來(lái),從而避免了跨線程訪問(wèn)。
當(dāng)運(yùn)行后臺(tái)線程,你能夠在用戶界面了解任務(wù)的進(jìn)度。
3.2、實(shí)施單機(jī)ArcObjects的應(yīng)用程序
正如微軟開發(fā)人員網(wǎng)絡(luò)(MSDN)網(wǎng)站上所說(shuō),“在.NET Framework版本2.0中,如果線程的單元狀態(tài)在啟動(dòng)前尚未確定,新的線程就初始化為ApartmentState.MTA。主應(yīng)用程序線程默認(rèn)初始化為ApartmentState.MTA。您不能通過(guò)設(shè)置代碼的第一行Thread.ApartmentState屬性再設(shè)置主應(yīng)用程序線程到ApartmentState.STA。而應(yīng)使用STAThreadAttribute代替。”
作為ArcObjects的開發(fā)人員,這意味著,如果您的應(yīng)用程序不被視為一個(gè)單一線程應(yīng)用程序初始化的,.NET框架將為所有的ArcObjects創(chuàng)建一個(gè)特殊的單線程單元(STA)線程,因?yàn)樗麄儽粯?biāo)記STA。這將導(dǎo)致對(duì)每一個(gè)從應(yīng)用程序調(diào)用ArcObjects的線程切換到這個(gè)特定的線程上來(lái)。反過(guò)來(lái),這迫使ArcObjects組件合在一起調(diào)用,并最終以COM組件調(diào)用可能慢了約50倍。幸運(yùn)的是,這可避免通過(guò)簡(jiǎn)單地標(biāo)記主要功能為[STAThread]。
3.3、使用托管線程池和BackgroundWorker的線程
線程池線程都是后臺(tái)線程。線程池通過(guò)提供一個(gè)由系統(tǒng)管理的應(yīng)用程序線程池使你使用線程更有效率。利用為每個(gè)任務(wù)創(chuàng)建一個(gè)新線程的線程池的優(yōu)點(diǎn)是線程創(chuàng)建和銷毀的開銷是可忽略的,它可以帶來(lái)更好的性能和更好的系統(tǒng)穩(wěn)定性。
然而,設(shè)計(jì)的所有ThreadPool線程是在多線程單元(MTA),因此不應(yīng)該被用來(lái)運(yùn)行ArcObjects,它們是單線程單元。若要解決此問(wèn)題,您有幾種選擇。一個(gè)是實(shí)現(xiàn)一個(gè)專用ArcObjects的線程,它被標(biāo)記為STAThread并委派每個(gè)從MTA線程調(diào)用這個(gè)專用ArcObjects線程。另一種解決方案是使用自定義的STA線程池的實(shí)現(xiàn),如標(biāo)記為STA線程的線程數(shù)組來(lái)運(yùn)行 ArcObjects。
3.4、同步運(yùn)行線程的并發(fā)執(zhí)行
在許多情況下,您必須同步執(zhí)行的并發(fā)運(yùn)行的線程。通常,你要等待一個(gè)或多個(gè)線程完成他們的任務(wù),當(dāng)一定條件下得到滿足,一個(gè)等待線程的信號(hào)恢復(fù)其任務(wù),條件如:測(cè)試是給定線否程激活和運(yùn)行,改變線程優(yōu)先級(jí),或給予其他一些條件。
在.NET中有幾種方法來(lái)管理運(yùn)行線程的執(zhí)行。可用來(lái)幫助線程管理的主要幾類如下:
System.Threading.Thread;
System.Threading.WaitHandle;
System.Threading.Monitor;
System.Threading.AutoResetEvent and System.Threading.ManualResetEvent。
3.5、在多個(gè)線程共享一個(gè)托管類型
有時(shí)候你的.NET應(yīng)用程序的底層數(shù)據(jù)結(jié)構(gòu)將是一個(gè)如DataTable或哈希表管理的對(duì)象。這些.NET托管對(duì)象允許你在多個(gè)線程共享數(shù)據(jù)獲取,如線程和主線程渲染他們。但是,您應(yīng)該咨詢MSDN Web站點(diǎn)以驗(yàn)證這一點(diǎn)是否是線程安全的。在許多情況下,一個(gè)對(duì)象是線程讀安全,而寫并不安全。有些集合實(shí)施同步方法,它提供了一個(gè)底層集合的同步包裝。
在你的對(duì)象被多個(gè)線程訪問(wèn)的情況下,根據(jù)MSDN關(guān)于這種情況的對(duì)象線程安全規(guī)則,你應(yīng)該獲得一個(gè)獨(dú)占鎖。取得這樣的獨(dú)占鎖能夠完成上面所描述的同步方法,或使用lock語(yǔ)句,它通過(guò)獲取給定對(duì)象的相互排他鎖標(biāo)簽一個(gè)關(guān)鍵塊。它可以確保,如果另一個(gè)線程試圖訪問(wèn)對(duì)象時(shí),它會(huì)被阻塞,直到該對(duì)象被釋放(退出鎖)。
3.6、從后臺(tái)線程更新用戶界面
在大多數(shù)情況下,您正在使用一個(gè)后臺(tái)線程來(lái)執(zhí)行長(zhǎng)時(shí)間的操作,你想向用戶報(bào)告進(jìn)度,狀態(tài),錯(cuò)誤或其他與該線程執(zhí)行的任務(wù)相關(guān)的信息。這可以通過(guò)更新一個(gè)應(yīng)用程序的用戶界面控件來(lái)實(shí)現(xiàn)。但是,在Windows中,窗體控件綁定到一個(gè)特定的線程(通常是主線程),并且不是線程安全的。因此,你必須委派,從而結(jié)合,任何調(diào)用UI控件的線程來(lái)控制它的所屬。該委托是通過(guò)調(diào)用Control.Invoke方法,該方法在線程上執(zhí)行委托,該委托擁有控件的基礎(chǔ)窗口句柄。要驗(yàn)證調(diào)用者是否必須調(diào)用Invoke方法,你可以使用屬性Control.InvokeRequired。您必須確保該控件的句柄再嘗試調(diào)用Control.Invoke或Control.InvokeRequired之前已經(jīng)創(chuàng)建。
3.7、從一個(gè)線程調(diào)用ArcObjects而不是主線程
在許多多線程應(yīng)用程序中,你將需要從不同線程調(diào)用AO組件。例如,你可能有一個(gè)后臺(tái)線程來(lái)獲取Web服務(wù),這反過(guò)來(lái),應(yīng)該增加新的項(xiàng)目到地圖顯示,響應(yīng)更改地圖,或運(yùn)行的geoprocessing(gp)的工具來(lái)執(zhí)行某些類型分析。
一個(gè)非常常見的情況是從一個(gè)計(jì)時(shí)器事件處理方法調(diào)用ArcObjects。計(jì)時(shí)器的Elapsed事件是在一個(gè)線程池的任務(wù)提出,這不是一個(gè)主線程。然而,它需要使用ArcObjects,這好像是需要跨單元調(diào)用。然而,這可以避免處理ArcObjects的組件,就好像AO組件是一個(gè)用戶界面控件和使用Invoke來(lái)調(diào)用委派到創(chuàng)建ArcObjects組件的主線程中。因此,沒(méi)有跨單元調(diào)用。
ISynchronizeInvoke接口包括的方法有Invoke,BeginInvoke,和EndInvoke。自己實(shí)現(xiàn)這些方法可能是一個(gè)艱巨的任務(wù)。相反,你應(yīng)該有你直接從System.Windows.Forms.Control繼承的類或者你應(yīng)該有一個(gè)助手類,它繼承自控件。要么選擇將提供一個(gè)簡(jiǎn)單而有效的對(duì)于調(diào)用方法的解決方案。
?
delegate SomethingClassType SomeDelegate(IArray array); SomeDelegate del = new SomeDelegate(AnotherFunc);//AnotherFunc與SomeDelegate同樣的形式 IAsyncResult ireslt = del.BeginInvoke(array, null, null);//異步操作 ProgressbarForm form = new ProgressbarForm();//異步操作中的進(jìn)度條窗體 form.Show(); System.Windows.Forms.Application.DoEvents(); while (!ireslt.IsCompleted) { System.Windows.Forms.Application.DoEvents(); } SomethingClassType something= del.EndInvoke(ireslt); form.Close();?以上是理論方面的闡述及一個(gè)本人開發(fā)過(guò)程中的一個(gè)代碼片段,希望這些能夠幫助你完成你的多線程程序。參考的資料如下:Windows MSDN,ESRI 的開發(fā)者網(wǎng)站。
?
?
?
總結(jié)
以上是生活随笔為你收集整理的ArcGIS Engine 中的多线程使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Hive自定义UDF和聚合函数UDAF
- 下一篇: Mycat1.6之注解多租户