Net托管世界的应用程序域和线程
Managed?Host??CLR(Common Language Runtime)? AppDomain?Assembly Thread
一、?引子
.Net框架提供了全新的計(jì)算平臺(tái),給出了一致性的面向?qū)ο蟮木幊汰h(huán)境,解決了安全、版本控制等原來(lái)系統(tǒng)平臺(tái)中存在的問(wèn)題,通過(guò)公用語(yǔ)言運(yùn)行庫(kù)(CLR)提供了一個(gè)高效、安全的程序執(zhí)行環(huán)境,也就是托管(也稱(chēng)作受控,Managed)環(huán)境。在這個(gè)類(lèi)似虛擬機(jī)環(huán)境下,我們編寫(xiě)的程序是如何運(yùn)行、如何“托管”的呢?這個(gè)托管的世界如何同非托管的世界相互聯(lián)系呢?
二、?如何進(jìn)入“托管”世界
首先,我們要了解,到目前為止,還沒(méi)有“純天然的”.net執(zhí)行環(huán)境(不排除類(lèi)似Java芯片的.net芯片將來(lái)會(huì)有),所謂托管的環(huán)境(CLR)需要運(yùn)行在當(dāng)前已存非托管的系統(tǒng)上。要進(jìn)入托管的.net世界,需要有一個(gè)稱(chēng)為宿主(Host)的程序?yàn)閷⒁\(yùn)行的.net托管代碼準(zhǔn)備執(zhí)行環(huán)境—也就是要加載.net世界的基礎(chǔ)CLR。在目前的windows系統(tǒng)上,能夠擔(dān)負(fù)這個(gè)重任的有3類(lèi)已存程序:
1、?shell(通常是Explorer),提供從用戶(hù)桌面啟動(dòng).net程序,創(chuàng)建一個(gè)進(jìn)程,啟動(dòng)此進(jìn)程建立CLR
2、?瀏覽器宿主(Internet Explorer),處理從web下載的.net代碼執(zhí)行。
3、?服務(wù)器宿主(如IIS的輔助進(jìn)程aspnet_wp.exe)
在執(zhí)行任何托管代碼之前,宿主必須首先加載并初始化公共語(yǔ)言運(yùn)行庫(kù)。假設(shè)一個(gè).net可執(zhí)行程序(prj1.exe)從shell啟動(dòng),操作系統(tǒng)會(huì)首先建立一個(gè)進(jìn)程,也就是宿主進(jìn)程。裝載的程序文件包括了在執(zhí)行配置信息和執(zhí)行代碼,代碼入口通常會(huì)被(創(chuàng)建此.net應(yīng)用程序的編譯器)放置一個(gè) Stub,這個(gè)Stub實(shí)際上就是一個(gè)6字節(jié)的本機(jī)代碼:
??jmp ?_CorExeMain
而_CorExeMain是從外部庫(kù)MSCorEE.dll導(dǎo)出,由prj1.exe引入的函數(shù),于是操作系統(tǒng)會(huì)裝入MSCorEE.dll(進(jìn)入.net世界的序曲),修正_CorExeMain的運(yùn)形時(shí)實(shí)際位置。
MSCorEE.dll實(shí)際上是一個(gè)COM組件庫(kù)。調(diào)用_CorExeMain后開(kāi)始初始化CLR,并察看prj1.exe的CLR相關(guān)數(shù)據(jù)結(jié)構(gòu),確定執(zhí)行.net托管代碼的入口。宿主調(diào)用.net 支持API CorBindToRuntimeEx來(lái)裝載CLR,并且根據(jù)配置初始化CLR的運(yùn)行特征,譬如垃圾回收策略等,這是因?yàn)椴煌乃拗髅媾R的應(yīng)用需求不一樣,一個(gè)服務(wù)宿主同普通的工作站宿主的“垃圾回收”(GC)機(jī)制顯然不一樣,所以啟動(dòng)CLR時(shí)的參數(shù)也不一樣。
宿主裝載的是一個(gè)符合COM規(guī)范要求的組件庫(kù)文件MSCorEE.dll,也就是CLR,一般存在于操作系統(tǒng)目錄(便于裝載)。有關(guān)mscoree.dll更多的了解,建議可看看FrameworkSDK目錄中的頭文件mscoree.h。MSCorEE.dll通過(guò)提供啟動(dòng)CLR的接口給宿主,譬如ICorRuntimeHost接口可用來(lái)配置運(yùn)行庫(kù)的各個(gè)方面(如垃圾回收),以將其加載到進(jìn)程中或注冊(cè)附加的事件,其中的start/stop方法可以讓宿主控制CLR在宿主進(jìn)程的生存期。
其實(shí)我們可以利用CorBindToRuntimeEx編寫(xiě)實(shí)現(xiàn)自己的宿主,關(guān)于.net宿主的實(shí)現(xiàn)可以單獨(dú)作為一個(gè)題目,在此給出幾個(gè)URL供大家參考。
?http://www.codeproject.com/dotnet/simpleclrhost.asp
http://www.elitevb.com/content/print.aspx?contentid=95
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/grfuncorbindtoruntimeex.asp
至此,我們明白, CLR——無(wú)所不能的虛擬機(jī)以DLL形式“寄生”于某個(gè)非托管世界的進(jìn)程!所謂托管世界實(shí)際上實(shí)指在Mscoree.dll建立的可控制環(huán)境下。難怪精通VCL的用戶(hù)說(shuō)CLR就是MS版的VCL!
?
三、?應(yīng)用程序域(AppDomain)和域中的線(xiàn)程(Thread)
一旦CLR加載并初始化完成,即宣告進(jìn)入.net世界。
在執(zhí)行第一個(gè)入口函數(shù)(通常是編譯時(shí)指定的Main函數(shù))之前,CLR會(huì)檢測(cè)該函數(shù)引用到的所有類(lèi)型,并且由CLR從堆中申請(qǐng)一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),通過(guò)這個(gè)數(shù)據(jù)結(jié)構(gòu)CLR管理所有引用到的類(lèi)型的訪問(wèn),這也就是托管的根本機(jī)制。每一個(gè)類(lèi)型的方法都會(huì)有一個(gè)條目供檢索和引用,在條目中保存該方法的代碼位置。在第一次執(zhí)行某個(gè)函數(shù)時(shí),屬于CLR服務(wù)的JITCompiler函數(shù)被調(diào)用,負(fù)責(zé)根據(jù)條目得到代碼,且將所有IL代碼驗(yàn)證后編譯成本機(jī)代碼。.net中每一個(gè)對(duì)象無(wú)論大小,都存在一個(gè)在當(dāng)前CLR中的唯一hash碼,可以將此hash碼作為類(lèi)似數(shù)據(jù)庫(kù)關(guān)鍵字段來(lái)區(qū)分代表每一個(gè)對(duì)象,.net的基類(lèi)Object. GetHashCode()可以幫助獲得當(dāng)前對(duì)象的hash碼。如果以后調(diào)用此方法函數(shù),那么將會(huì)直接調(diào)用這些本機(jī)代碼。此過(guò)程就是所謂的“即時(shí)編譯”。每一個(gè)托管對(duì)象的方法都會(huì)在調(diào)用前被CLR的Jit機(jī)制編譯成本地代碼,然后交給操作系統(tǒng)調(diào)度CPU執(zhí)行。理解托管代碼在運(yùn)行時(shí)的細(xì)節(jié),可以幫助我們深入了解域和線(xiàn)程的本質(zhì)以及他們相互之間的關(guān)系。
所謂IL驗(yàn)證主要做安全檢查,大體上是根據(jù)托管代碼元數(shù)據(jù)及CLR規(guī)則來(lái)檢驗(yàn)IL代碼,譬如某個(gè)變量是否初始化,調(diào)用某個(gè)函數(shù)時(shí)攜帶的參數(shù)是否正確,方法調(diào)用是否總能夠返回等等。通常情況下,IL驗(yàn)證會(huì)在遇到它認(rèn)為不安全的代碼時(shí)拋出VerificationException異常,阻止代碼繼續(xù)執(zhí)行。
初始化同時(shí),CLR建立第一個(gè)AppDomain,即默認(rèn)應(yīng)用程序域,在此域中執(zhí)行入口代碼。從概念上講,應(yīng)用程序域是.net 托管世界中的“應(yīng)用程序在其中執(zhí)行的獨(dú)立環(huán)境”,是要執(zhí)行或引用的多個(gè)程序集的容器(一個(gè)應(yīng)用程序域肯定不止加載一個(gè)程序集)。千萬(wàn)不要理解成進(jìn)程的概念,應(yīng)用程序域存在于CLR中,而CLR屬于宿主進(jìn)程,應(yīng)用程序域同進(jìn)程屬于不同層次上的概念。但的確,.net的設(shè)計(jì)者是仿照操作系統(tǒng)的進(jìn)程概念來(lái)設(shè)計(jì)應(yīng)用程序域的,使得AppDomain成為.net世界的執(zhí)行單位,相互之間代碼執(zhí)行隔絕。大體上,下圖可以幫助理解宿主、CLR、應(yīng)用程序域之間的關(guān)系。
?
圖一
為了管理裝載的程序集,每一個(gè)應(yīng)用程序域都有自己的配置信息和存儲(chǔ)區(qū)域、引用、執(zhí)行邊界,另外重要的是有自己的安全策略。安全策略存儲(chǔ)在證據(jù)(evidence)中,所有在同一AppDomain內(nèi)的程序集都會(huì)共享這些資源和安全指示信息。前面講到的IL驗(yàn)證會(huì)根據(jù)這些配置、程序集中的元數(shù)據(jù)和證據(jù)檢驗(yàn)代碼,確保應(yīng)用程序域的代碼不會(huì)“越界”,不會(huì)破壞另外域的對(duì)象,也確保不會(huì)有C++世界中可怕的無(wú)效、錯(cuò)誤指針的危險(xiǎn)。
通常情況下,只有一個(gè)應(yīng)用程序域的.net程序大多數(shù)行為類(lèi)似傳統(tǒng)的非托管世界而執(zhí)行,在默認(rèn)應(yīng)用程序域結(jié)束后,進(jìn)程結(jié)束,CLR像其他DLL一樣被卸載。然而如果存在多個(gè)域,那么情況就要復(fù)雜些——起碼,CLR必須等到所有的應(yīng)用程序域都結(jié)束后才可能按照DLL規(guī)則釋放。另外,不像進(jìn)程間那樣通信艱難,同一個(gè)操作系統(tǒng)進(jìn)程內(nèi)的應(yīng)用程序域間可以進(jìn)行花費(fèi)較低的交互,但是受限應(yīng)用程序域的分隔特性,不可以在一個(gè)應(yīng)用程序域直接操作另外一個(gè)域的實(shí)例對(duì)象(對(duì)象同程序集、應(yīng)用程序域緊密相關(guān),CLR會(huì)“照管”他們,確保應(yīng)用程序域代碼執(zhí)行安全)。
域間的對(duì)象引用有兩種情況:如果域間引用對(duì)象自身是傳值的,那么對(duì)象必須支持序列化(實(shí)現(xiàn)接口System.ISerializable), 在穿越域引用被另外域的對(duì)象時(shí),會(huì)被序列化,到達(dá)目標(biāo)域后反序列化,此時(shí),會(huì)因反序列化從而加載該對(duì)象定義所在的程序集。如果是傳遞引用類(lèi)型的對(duì)象,在目的域(需要引用該對(duì)象的域,非生成此對(duì)象的容器域)的邊界會(huì)建立一個(gè)實(shí)例對(duì)象的代理,原來(lái)的對(duì)象仍然存在,“安然”存活于被引用域,但是彼域中的實(shí)例對(duì)象代理(也就是封裝器wrapper)知道如何同此對(duì)象實(shí)例交流,此代理是通過(guò)CLR的提供的服務(wù)來(lái)調(diào)度實(shí)例對(duì)象的方法。如果對(duì)象并非支持序列化的傳值或者支持傳引用的類(lèi)型,或者由于加載對(duì)象定義的所在程序集失敗(該程序集無(wú)法定位或者由于應(yīng)用程序域安全限制而無(wú)法加載),會(huì)導(dǎo)致拋出異常,通常表示這是一個(gè)不合法的跨域?qū)ο笠貌僮鳌1M管可能所有的應(yīng)用程序域都是存在于同一個(gè)操作系統(tǒng)進(jìn)程,但是仍然由于CLR對(duì)于應(yīng)用程序域的安全隔離而導(dǎo)致一些損耗,如果沒(méi)有必要,要盡可能避免這樣做。
應(yīng)用程序域是.net CLR世界的輕量子進(jìn)程,而線(xiàn)程卻是操作系統(tǒng)分配處理器時(shí)間的基本單元。線(xiàn)程們根據(jù)一定的優(yōu)先級(jí)規(guī)則被操作系統(tǒng)調(diào)度,切換時(shí)都需要保持線(xiàn)程的執(zhí)行上下文。所謂上下文是使線(xiàn)程在線(xiàn)程的宿主進(jìn)程地址空間中無(wú)縫地繼續(xù)執(zhí)行所需的所有信息,包括線(xiàn)程的 CPU 寄存器組和堆棧。在應(yīng)用程序域同樣可以在主線(xiàn)程外創(chuàng)建多個(gè)子線(xiàn)程,.Net的Thread 和ThreadPool類(lèi)提供了對(duì)操作系統(tǒng)的線(xiàn)程的包裝,大大簡(jiǎn)化了對(duì)于線(xiàn)程的使用難度。當(dāng)我們?cè)谝粋€(gè)應(yīng)用程序域的主線(xiàn)程創(chuàng)建了其他子線(xiàn)程時(shí),這些線(xiàn)程屬于當(dāng)前進(jìn)程。從操作系統(tǒng)角度來(lái)看,線(xiàn)程無(wú)所謂專(zhuān)屬于某個(gè)應(yīng)用程序域,一個(gè).net程序創(chuàng)建的進(jìn)程中的線(xiàn)程實(shí)際上都是屬于同一個(gè)進(jìn)程的,這也導(dǎo)致線(xiàn)程不必唯一存在于某個(gè)應(yīng)用程序域中,而是可以根據(jù)需要在線(xiàn)程執(zhí)行生命期間處于多個(gè)應(yīng)用程序域(但任一時(shí)刻只能屬于某一個(gè)特定域)。應(yīng)用程序域和線(xiàn)程不存在一對(duì)一的關(guān)系,一個(gè)線(xiàn)程可以跨越多個(gè)應(yīng)用程序域,而一個(gè)應(yīng)用程序域可以通過(guò)加載程序集的類(lèi)實(shí)例對(duì)象而創(chuàng)建多個(gè)線(xiàn)程。應(yīng)用程序域的方法調(diào)用、對(duì)象解析、IL的驗(yàn)證執(zhí)行等受CLR的控制,所以應(yīng)用程序域之間可以做到相互阻隔。但線(xiàn)程屬于操作系統(tǒng)調(diào)度的,沒(méi)有應(yīng)用程序域的負(fù)擔(dān),可以迅速切換被系統(tǒng)調(diào)度執(zhí)行。
當(dāng)有需要線(xiàn)程從一個(gè)域訪問(wèn)另外一個(gè)域的對(duì)象或者執(zhí)行另外域的對(duì)象的代碼時(shí),實(shí)際上屬于域間通信問(wèn)題。因此,會(huì)在當(dāng)前域創(chuàng)建一個(gè)ObjectHandle類(lèi)型的代理(見(jiàn)上面的討論),然后利用ObjectHandle的成員方法Unwrap得到目標(biāo)對(duì)象的引用,通過(guò)引用執(zhí)行目標(biāo)代碼。由于所有的托管代碼執(zhí)行實(shí)際上都經(jīng)過(guò)了CLR的檢查,此時(shí),CLR會(huì)判斷到域間操作是同一個(gè)(宿主)進(jìn)程,因而允許當(dāng)前線(xiàn)程代碼執(zhí)行,執(zhí)行目標(biāo)域的對(duì)象的方法代碼,從而實(shí)現(xiàn)了“穿越”域。實(shí)際上,如果發(fā)生對(duì)象域間調(diào)/引用的域不是屬于同一個(gè)進(jìn)程,那么域間的通信采用.net Remotting技術(shù)而不是直接通過(guò)當(dāng)前線(xiàn)程代碼跳轉(zhuǎn)到另一個(gè)域。如果拋開(kāi)CLR的角度,從操作系統(tǒng)角度看,這一切純粹是普通的進(jìn)程內(nèi)部的線(xiàn)程調(diào)度。域間的“消耗”完全是因?yàn)镃LR這個(gè).net世界的“上帝”在檢查代碼的安全執(zhí)行(但是是必須的),具體的細(xì)節(jié)大致是:因?yàn)樗型泄艿膶?duì)象、方法之類(lèi)的都在CLR中存在條目,執(zhí)行一個(gè)對(duì)象的方法,會(huì)自動(dòng)由CLR查找條目,受到CLR的管理監(jiān)控,然后決定執(zhí)行本地代碼。CLR判斷出當(dāng)前條目屬于哪個(gè)域,就將域的私有存儲(chǔ)和資源分配給當(dāng)前線(xiàn)程(線(xiàn)程利用靜態(tài)方法AppDomain.CurrentDomain可以得到當(dāng)前所處應(yīng)用程序域?qū)ο?#xff09;。實(shí)際上,涉及到跨域的引用和執(zhí)行,應(yīng)用程序域之間的交互,無(wú)論是同一個(gè)進(jìn)程的多個(gè)域還是不同進(jìn)程間的域,乃至不同機(jī)器間域,通信通常根據(jù)域的分布區(qū)分對(duì)待,由于CLR能夠區(qū)分要跳轉(zhuǎn)執(zhí)行的代碼是同一進(jìn)程還是同一機(jī)器,所以同一進(jìn)程的跨域線(xiàn)程調(diào)度實(shí)際上使得線(xiàn)程在邊界代理經(jīng)過(guò)CLR的檢查后就跳轉(zhuǎn)到目標(biāo)域,執(zhí)行相應(yīng)的本機(jī)代碼。
我們可以結(jié)合下圖進(jìn)一步理解線(xiàn)程同域之間的關(guān)系:
?
??注意圖中其中Thread2在生命期間“穿越“了主AppDomain和多個(gè)子AppDomain。Thread3整個(gè)生命周期卻只存在AppDomain1中。AppDomain1有3個(gè)線(xiàn)程運(yùn)行,Thread1和Thread2執(zhí)行期間轉(zhuǎn)移到AppDomain2執(zhí)行,其間Thread1和Thread2通過(guò)代理執(zhí)行引用對(duì)象的代碼,從而將線(xiàn)程切換到AppDomain2。Thread2最初從默認(rèn)域執(zhí)行,執(zhí)行期間跨越默認(rèn)域、AppDomain1和AppDomain2。
可能影響代碼執(zhí)行流的還有一個(gè)因素——異常。當(dāng)應(yīng)用程序域需要被卸載時(shí),應(yīng)用程序域的所有線(xiàn)程會(huì)被CLR通知,CLR會(huì)在當(dāng)前域的線(xiàn)程強(qiáng)行發(fā)出一個(gè)ThreadAbortException異常,迫使他們退出該應(yīng)用域。另一方面,CLR會(huì)檢查所有涉及到此域的其他域中的代理對(duì)象,將其設(shè)定為無(wú)效。以后,凡是想通過(guò)這些代理實(shí)現(xiàn)其他域?qū)ο蟮恼{(diào)用或引用,將會(huì)引發(fā)AppDomainUnloadedException異常。應(yīng)用程序域的卸載可以由自身或者自身的子線(xiàn)程發(fā)出,也可以由其他域的代碼發(fā)出(譬如在一個(gè)主應(yīng)用程序域創(chuàng)建子域后然后卸載子域)。如果由于種種原因不能夠卸載應(yīng)用程序域,那么會(huì)產(chǎn)生CannotUnloadAppDomainException異常。如果是應(yīng)用程序域自身(包括應(yīng)用程序域創(chuàng)建的子線(xiàn)程)發(fā)出卸載命令,那么會(huì)由CLR來(lái)創(chuàng)建一個(gè)新的線(xiàn)程執(zhí)行卸載應(yīng)用程序域,產(chǎn)生的異常由CLR發(fā)出的線(xiàn)程捕捉;如果是另外的域中代碼發(fā)出卸載命令,那異常會(huì)轉(zhuǎn)交給發(fā)出卸載命令的線(xiàn)程。清楚了解異常的拋送路線(xiàn),可以確保我們寫(xiě)出安全可靠穩(wěn)定的應(yīng)用程序。
四、?AppDomain小結(jié)
實(shí)際上我們看到,.net的托管環(huán)境CLR是通過(guò)將COM DLL文件msCorEE.dll裝入當(dāng)前操作系統(tǒng)進(jìn)程來(lái)建立的。托管世界的執(zhí)行對(duì)象提供元數(shù)據(jù)和IL代碼以及安全證據(jù)等在CLR的內(nèi)存對(duì)執(zhí)行,所有的托管代碼經(jīng)過(guò)Jit編譯成本地代碼,由于CLR的一切對(duì)象被“托管”可以確保AppDomain的實(shí)現(xiàn)類(lèi)似進(jìn)程環(huán)境的執(zhí)行分隔作用,CLR可以根據(jù)應(yīng)用程序域的邊界阻止任何不安全的訪問(wèn)。CLR也跟蹤管理托管線(xiàn)程,線(xiàn)程可以通過(guò)域間的通信功能實(shí)現(xiàn)線(xiàn)程在多個(gè)應(yīng)用程序域上的移動(dòng)。進(jìn)程和線(xiàn)程屬于操作系統(tǒng)的調(diào)度執(zhí)行單元,但應(yīng)用程序域?qū)儆?net的執(zhí)行邏輯單元,通過(guò)“托管”實(shí)現(xiàn)應(yīng)用程序域的代碼執(zhí)行以及域間數(shù)據(jù)訪問(wèn)的分隔。
通常情況下,我們的.net應(yīng)用程序僅僅需要一個(gè)缺省的應(yīng)用程序域,但也有一些情況考慮建立其他應(yīng)用程序域可能更好一些:
1、? 需要隔離的程序集,譬如一些特別容易引起崩潰的代碼可以考慮單獨(dú)運(yùn)行于一個(gè)特定的AppDomain
2、? 不同安全級(jí)別的程序集,如果需要為自己的代碼劃分安全執(zhí)行的邊界,可以考慮將不同安全級(jí)別的代碼單獨(dú)創(chuàng)建于某個(gè)設(shè)定了不同安全信息的appDomain
3、? 從性能上考慮,有些程序集可能會(huì)消耗大量資源,盡管在托管環(huán)境下,基本上不存在資源消耗漏洞,但是總會(huì)存在特定時(shí)間訪問(wèn)密集造成消耗大量資源的情況,這時(shí)可以考慮創(chuàng)建單獨(dú)的AppDomain,在資源消耗超過(guò)臨界點(diǎn)后進(jìn)行AppDomain的卸載,適應(yīng)系統(tǒng)運(yùn)行要求。
4、? 不同版本的同一應(yīng)用程序集的同時(shí)運(yùn)行。這個(gè)在COM時(shí)代是一個(gè)大問(wèn)題,現(xiàn)在通過(guò)AppDomain,實(shí)現(xiàn)了在一個(gè)進(jìn)程中執(zhí)行版本不同的兩個(gè)程序集,可以做到良好的兼容性。
5、動(dòng)態(tài)加載一些程序。可以將一些不經(jīng)常使用的程序集動(dòng)態(tài)載入,(為了效率)經(jīng)常使用的程序集則可以動(dòng)態(tài)加載,甚至單獨(dú)加載到應(yīng)用程序域中去。
6、共用程序集,提高.net的執(zhí)行效率。譬如我們用到的System.object System.Int32 等對(duì)象往往多個(gè)應(yīng)用程序域都需要,會(huì)造成資源浪費(fèi),為減少資源使用,含有這些常用.net類(lèi)的程序集MSCorLib.dll會(huì)以單獨(dú)的中立域的方式加載,CLR會(huì)為其維護(hù)一個(gè)特殊的加載器,使得這個(gè)程序集只有在進(jìn)程中斷時(shí)才會(huì)被卸載,從而提高了速度,減少了內(nèi)存等資源的浪費(fèi)。
五、?參考
本文大致討論了.net CLR托管的執(zhí)行機(jī)制,以及程序集如何加載到應(yīng)用程序域執(zhí)行、線(xiàn)程等如何在托管集之下的執(zhí)行,試圖幫助讀者理解托管的計(jì)算環(huán)境代碼執(zhí)行的較為詳細(xì)情形。但是,并沒(méi)有試圖詳細(xì)闡述.net框架的體系細(xì)節(jié),甚至連具體的.net代碼都沒(méi)有提供分析,如果您需要進(jìn)一步了解.net的更多細(xì)節(jié),請(qǐng)參考閱讀以下的資料:
1、《.net 框架程序設(shè)計(jì)(修訂版)》 Jeffrey Richter 著/李建中?譯?清華大學(xué)出版社。
2、《.net核心技術(shù)—原理與架構(gòu)》?劉曉華 編著 電子工業(yè)出版社
3、MSDN .net 框架SDK
轉(zhuǎn)載于:https://www.cnblogs.com/hellolong/articles/5746741.html
總結(jié)
以上是生活随笔為你收集整理的Net托管世界的应用程序域和线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 关于创业公司产品开发原则
- 下一篇: this、new、模式工厂、创建新的构造