共用方式為


可靠性最佳做法

下列可靠性規則面向 SQL Server;不過,它們也適用於任何主機型伺服器應用程式。 SQL Server 之類的伺服器不會流失資源,而且不會關閉,這一點非常重要。 不過,無法針對每個改變對象狀態的方法撰寫回復代碼來完成。 目標並不是撰寫能在所有情況下、所有位置都具備回退機制以從錯誤中復原的100%可靠的託管程式碼。 這將是一項艱巨的任務,幾乎沒有成功的機會。 Common Language Runtime(CLR)無法輕易提供足夠的保證給受控程式碼,以實現撰寫完美程式碼的可能性。 請注意,與 ASP.NET 不同的是,SQL Server 僅使用一個無法回收的進程,而若要回收進程,會導致資料庫停機時間過長,這是不可接受的。

透過這些較弱的保證,在單一進程中運行時,可靠性基於在必要時終止線程或回收應用程式域,並採取預防措施,以確保不會流失例如句柄或記憶體等作業系統資源。 即使使用這個更簡單的可靠性條件約束,仍然有顯著的可靠性需求:

  • 絕不會洩漏作系統資源。

  • 識別所有形式中的所有管理的鎖定並將其傳送至 CLR。

  • 永不中斷跨應用程式域共享狀態,讓 AppDomain 回收能夠順利運作。

雖然理論上可以撰寫 Managed 程式代碼來處理 ThreadAbortExceptionStackOverflowExceptionOutOfMemoryException 例外狀況,但預期開發人員在整個應用程式中撰寫這類強固的程式代碼是不合理的。 因此,非同步例外狀況會導致正在執行的線程終止;如果被終止的線程正在編輯共享狀態,可以透過線程是否持有鎖定來判斷,則會卸載AppDomain。 當編輯共享狀態的方法終止時,狀態將會損毀,因為無法撰寫可靠的回傳程式代碼以更新至共享狀態。

在 .NET Framework 2.0 版中,唯一需要可靠性的主機是 SQL Server。 如果您的元件將在 SQL Server 上執行,您應該針對該元件的每個部分執行可靠性工作,即使資料庫中執行時已停用的特定功能也一樣。 這是必要的,因為程式代碼分析引擎會檢查元件層級的程序代碼,而且無法區分已停用的程序代碼。 另一個 SQL Server 程式設計上的考量是,SQL Server 將所有作業在單一進程中執行,並使用 AppDomain 回收來清除所有資源,如記憶體和操作系統資源。

您無法依賴完成項、解構函式或 try/finally 區塊來回退程式碼。 它們可能會中斷或不被呼叫。

異步例外狀況可能在非預期的位置拋出,甚至在每個機器指令中:ThreadAbortExceptionStackOverflowExceptionOutOfMemoryException

受控線程不一定是 SQL 中的 Win32 線程;它們可能是纖維。

全進程或跨應用程式域可變動的共享狀態極難安全地改變,而且應盡可能避免。

SQL Server 中記憶體不足的情況並不罕見。

如果裝載在 SQL Server 中的連結庫未正確更新其共享狀態,則程式代碼在資料庫重新啟動之前不會復原的機率很高。 此外,在某些情況下,這可能會造成 SQL Server 進程失敗,導致資料庫重新啟動。 重新啟動資料庫可能會關閉網站或影響公司作業,因而損害可用性。 操作系統資源(例如記憶體或控制代號)的緩慢洩漏可能會導致伺服器最終無法配置控制代號,並無法恢復,或者伺服器的效能可能會逐漸下降,降低客戶應用程式的可用性。 顯然,我們想要避免這些案例。

最佳做法規則

簡介著重於伺服器中執行的受控代碼程式代碼審查應該確保捕捉的內容,以增加框架的穩定性和可靠性。 所有這些檢查通常都是良好的作法,而在伺服器上更是絕對必要的。

面對死鎖或資源條件約束,SQL Server 會中止線程或卸除 AppDomain。 發生這種情況時,保證只會執行限制執行區域中的後端程序代碼。

使用 SafeHandle 以避免資源流失

AppDomain 卸除的情況下,您無法依賴 finally 區塊或完成項的執行,因此重要的是要透過 SafeHandle 類別來抽象化所有操作系統資源的存取,而不是使用 IntPtrHandleRef 或類似類別。 這可讓 CLR 追蹤並關閉您在拆除情境中使用的 AppDomain 句柄。 SafeHandle 將使用 CLR 必定執行的關鍵終結器。

操作系統句柄從創建之時起一直儲存在安全句柄中,直到釋放的那一刻。 沒有時間範圍允許 ThreadAbortException 發生以洩漏控制代碼。 此外,平臺調用會參考計數句柄,以允許密切追蹤句柄的存留期,防止與目前使用句柄的方法之間的 Dispose 競爭狀況發生安全性問題。

目前大多數用於清理作業系統控制代碼的類別不再需要終結器。 相反地,終結器將會在衍生類別中SafeHandle

請注意,SafeHandle 並非 IDisposable.Dispose 的替代品。 仍然存在潛在的資源爭用情況,因此明確釋放操作系統資源仍然具有效能上優勢。 請注意,明確處置資源的 finally 區塊可能無法執行到完成。

SafeHandle 可讓您實作自己的 ReleaseHandle 方法來執行工作以釋放句柄,例如將狀態傳遞至作系統句柄釋放例程,或在迴圈中釋放一組句柄。 CLR 保證會執行此方法。 實現的作者 ReleaseHandle 有責任確保在所有情況下都釋放控制代碼。 若未能這麼做,會導致控制代碼洩漏,這通常會導致與控制代碼相關聯的原生資源外洩。 因此,建構 SafeHandle 衍生類別時,必須仔細設計結構,以確保 ReleaseHandle 的實作不需要配置可能在叫用時無法取得的資源。 請注意,只要您的程式碼可以處理可能失敗的方法,並完成合約以釋放原生句柄,則允許在ReleaseHandle的實作中呼叫這些方法。 為了進行偵錯,ReleaseHandle具有Boolean回值,如果發生嚴重錯誤,因而無法釋放資源,Boolean可能會被設為。 這樣做會啟用 releaseHandleFailed MDA,如果已啟用,有助於找出問題。 它不會影響運行時間的其他方面;ReleaseHandle 不會針對相同的資源再次呼叫,因此會導致控制代碼洩漏。

SafeHandle 在某些內容中不合適。 ReleaseHandle由於此方法可以在終結器執行緒上GC執行,因此必須在特定執行緒上釋放的任何控制代碼都不應該包裝在SafeHandle中。

執行階段可呼叫包裝函式(RCWs)可由 CLR 清除,而不需要額外的程式碼。 對於使用平台調用並將 COM 物件視為 IUnknown*IntPtr 的程式代碼,應該重寫此程式代碼以使用 RCW。 SafeHandle 可能不適合這個情境,因為非受控釋放方法可能會回呼至受控程式碼。

程式代碼分析規則

使用 SafeHandle 來封裝作業系統資源。 請勿使用 HandleRef 或類型的 IntPtr欄位。

確保終結器不必執行,以防止作業系統資源洩漏

請仔細檢閱您的終結器,以確保即使它們沒有執行,重要的作業系統資源也不會外洩。 與在穩定狀態下執行應用程式或SQL Server等伺服器關機時正常的AppDomain卸除之情況有異,物件不會在突然的AppDomain卸載期間終結。 確保資源在發生突然卸載的情況下不會洩漏,因為無法保證應用程式的正確性,但必須以防止資源洩漏來維護伺服器的完整性。 使用 SafeHandle 釋放任何作業系統資源。

確定 finally 子句不必執行,以避免作業系統資源流失

finally 子句不保證會在 CER 之外執行,因此要求程式庫開發人員不依賴 finally 區塊內的程式代碼來釋放非受控資源。 使用 SafeHandle 是建議的解決方案。

程式代碼分析規則

請使用 SafeHandle 來清理操作系統資源,而非 Finalize。 請勿使用 IntPtr;使用 SafeHandle 來封裝資源。 如果 finally 子句必須執行,請將它放在 CER 中。

所有鎖定應該使用現有的管理鎖定程式碼進行處理。

CLR 必須知道程式代碼何時在鎖定中,以便知道要卸載 AppDomain ,而不只是中止線程。 中止執行緒可能會很危險,因為執行緒正在處理的資料可能處於狀態不一致。 因此,必須回收整個 AppDomain 。 無法識別鎖定的後果可能導致死結或結果不正確。 使用BeginCriticalRegionEndCriticalRegion方法來識別鎖定區域。 它們是類別上的 Thread 靜態方法,僅適用於目前的線程,有助於防止一個線程編輯另一個線程的鎖定計數。

EnterExit 內建此 CLR 通知,因此建議使用它們,並建議使用這些方法的 lock 陳述式

其他鎖定機制,例如旋轉鎖,以及AutoResetEvent必須呼叫這些方法,以通知 CLR 進入重要區段。 這些方法不會採取任何鎖定;他們會通知 CLR 程式代碼正在關鍵區段中執行,而中止線程可能會使共享狀態不一致。 如果您已定義自己的鎖定類型,例如自定義 ReaderWriterLock 類別,請使用這些鎖定計數方法。

程式代碼分析規則

使用 BeginCriticalRegionEndCriticalRegion 標記並識別所有鎖。 請勿在循環中使用 CompareExchangeIncrementDecrement。 請勿執行這些方法之 Win32 變體的平台調用。 請勿 Sleep 在迴圈中使用。 請勿使用易變關鍵字欄位。

清除程式碼必須位於 finally 或 catch 區塊中,而不是在 catch 區塊之後。

清除程式碼不應位於 catch 區塊之後,而應在 finallycatch 區塊之中。 這應該是一個正常的良好作法。 區塊 finally 通常是慣用的,因為它會在擲回例外狀況和通常遇到區塊結尾 try 時執行相同的程序代碼。 如果拋出未預期的異常,例如 ThreadAbortException,清除程式碼將不會執行。 任何您在 finally 中需要清理的非管理資源,最好包裹在 SafeHandle 中以防止洩漏。 請注意,C# using 關鍵詞可以有效地用來處置物件,包括句柄。

雖然 AppDomain 回收可以清除完成項線程上的資源,但仍務必將清除程序代碼放在正確的位置。 請注意,如果執行緒收到異步例外狀況而未持有鎖定,CLR 會嘗試結束執行緒本身,而無需回收 AppDomain。 確保資源及早清理,而不是延後處理,有助於更有效地提供更多資源,並更妥善地管理資源的使用壽命。 如果您在某些錯誤處理路徑中未明確關閉檔案的句柄,而是依賴 SafeHandle 終結器來清除它,那麼下一次程式碼執行時,如果終結器尚未運行,可能會導致嘗試存取完全相同檔案的失敗。 基於這個理由,確保清除程序代碼存在且運作正常,將有助於更乾淨且快速地從失敗中復原,即使並非絕對必要。

程式代碼分析規則

必須將清除程式代碼放在 catch 之後的 finally 區塊中。 在 finally 區塊中放置要處置的呼叫。 catch 區塊應該以擲回或重新擲回結尾。 雖然會有例外情況,例如程式碼在偵測能否建立網路連線時,可能會遇到多種例外狀況,但任何在正常情況下需要攔截多個例外的程式碼,應該給出指示以驗證程式碼是否會成功。

Process-Wide 應消除應用程式域之間的可變動共享狀態,或使用限制的執行區域

如簡介中所述,要撰寫能夠以可靠方式監視應用程式範圍內的不同域中的流程範圍共享狀態的受控代碼可能會非常困難。 全進程共享狀態是應用程式域之間任何一種共用的數據結構,無論是在 Win32 程式代碼中、CLR 內部,還是在使用遠端處理的 Managed 程式代碼中。 任何可變動的共享狀態都很難在 Managed 程式代碼中正確撰寫,而且任何靜態共享狀態可能只以非常謹慎的方式完成。 如果您有全進程或全計算機的共享狀態,請尋找某種方式來消除它,或使用限制的執行區域來保護共享狀態(CER)。 請注意,任何未識別和修正共享狀態的函式庫都可能導致主機,例如需要乾淨卸載AppDomain的 SQL Server 這樣的主機,發生崩潰。

如果程式代碼使用 COM 物件,請避免在應用程式域之間共用該 COM 物件。

鎖定無法跨進程或在應用程式域之間運作。

在過去,Enterlock語句已用來創建全域程序鎖。 例如,當鎖定 AppDomain 敏捷類別時,例如 Type 來自非共用組件的實例、Thread 對象、共用字串池中的字串及使用遠端處理跨應用程式域共用的某些字串,就會發生這種情況。 這些鎖定已不再是整個進程的。 若要識別整個進程間網域鎖定的存在,請判斷鎖定內的程序代碼是否使用任何外部保存的資源,例如磁碟上的檔案或資料庫。

請注意,在 AppDomain 內取得鎖定可能會導致問題,如果受保護的程式代碼使用外部資源,因為這些程式代碼可能會在多個應用程式域同時執行。 寫入一個記錄檔或系結至整個進程的套接字時,可能會發生問題。 這些變更表示,使用 Managed 程式代碼來取得程序全域鎖定的方式並不容易,除了使用具名的 MutexSemaphore 實例以外。 建立不會同時在兩個應用程式域中執行的程式代碼,或使用 MutexSemaphore 類別。 如果無法變更現有的程式代碼,請勿使用名為 Mutex 的 Win32 來達成此同步處理,因為以光纖模式執行表示您無法保證相同的作系統線程會取得並釋放 Mutex。 您必須使用受控的 Mutex 類別,或者具名的 ManualResetEventAutoResetEventSemaphore,以 CLR 能夠識別的方式來同步程式碼鎖定,而不是使用非受控程式碼來進行鎖定同步。

避免 lock(typeof(MyType))

共用元件中的私用和公用 Type 物件,其中只有一個跨所有應用程式域共用的程式代碼複本也存在問題。 對於共用元件,每個進程只有一個 Type 實例,這表示多個應用程式域共用完全相同 Type 的實例。 在 Type 例項上取得鎖定時,會取得影響整個進程的鎖定,而不只是 AppDomain。 如果某個 AppDomainType 物件取得鎖定,然後該線程突然中止,它將不會釋放鎖定。 然後,此鎖定可能會導致其他應用程式域死結。

在靜態方法中取得鎖定的好方法,包括將靜態內部同步處理物件新增至程序代碼。 如果類別建構函式存在,則可以在類別建構函式中初始化,但如果不存在,則可以像這樣初始化:

private static Object s_InternalSyncObject;
private static Object InternalSyncObject
{
    get
    {
        if (s_InternalSyncObject == null)
        {
            Object o = new Object();
            Interlocked.CompareExchange(
                ref s_InternalSyncObject, o, null);
        }
        return s_InternalSyncObject;
    }
}

然後在取得鎖定時,使用 InternalSyncObject 屬性取得要鎖定的物件。 如果您已在類別建構函式中初始化內部同步處理物件,就不需要使用 屬性。 雙重檢查鎖定初始化碼看起來應該像下列範例:

public static MyClass SingletonProperty
{
    get
    {
        if (s_SingletonProperty == null)
        {
            lock(InternalSyncObject)
            {
                // Do not use lock(typeof(MyClass))
                if (s_SingletonProperty == null)
                {
                    MyClass tmp = new MyClass(…);
                    // Do all initialization before publishing
                    s_SingletonProperty = tmp;
                }
            }
        }
        return s_SingletonProperty;
    }
}

關於 lock(this) 的注意事項

通常可以接受對可公開存取的個別物件加鎖。 不過,如果物件是單例物件並且可能導致整個子系統死結,請考慮使用上述設計模式。 例如,對SecurityManager對象的鎖定可能會導致AppDomain內部發生死鎖,使整個AppDomain無法使用。 最好不要鎖定此類型的可公開存取物件。 不過,個別集合或陣列的鎖定通常不會發生問題。

程式代碼分析規則

請勿鎖定可能會在不同應用程式域中使用的類型,或那些缺乏明確身份識別的類型。 請勿在 EnterTypeMethodInfoPropertyInfoStringValueType或衍生自 Thread的任何物件上呼叫 MarshalByRefObject

拿掉 GC.KeepAlive 呼叫

現有的許多程式碼要麼在需要使用 KeepAlive 時沒有使用,要麼在不應該使用時卻使用了。 轉換成 SafeHandle 之後,類別不需要呼叫 KeepAlive,假設它們沒有終結器,而是依賴 SafeHandle 完成操作系統的句柄。 雖然保留呼叫KeepAlive的效能成本可能微不足道,但將KeepAlive呼叫視為解決可能已不存在的生命週期問題的必要或充分條件,會使程式碼更難以維護。 不過,當使用 COM Interop CLR 呼叫包裝物件(RCW)時,程式碼仍然需要 KeepAlive

程式代碼分析規則

移除 KeepAlive

使用 HostProtection 屬性

HostProtectionAttribute (HPA)使用宣告式安全性動作來判斷主機保護需求,讓主機防止即使是完全信任的程式碼也進行不適合的呼叫,這些方法不適合指定的主機,例如 SQL Server 中的 ExitShow

HPA 只會影響那些裝載通用語言運行庫並實作主機保護的非受控應用程式,例如 SQL Server。 套用時,安全性動作會導致根據類別或方法公開的主機資源建立連結需求。 如果程式代碼是在用戶端應用程式中執行,或在未受主機保護的伺服器上執行,則屬性會「蒸發」;它不會偵測到,因此不會套用。

這很重要

此屬性的目的是強制執行主機特定的程序設計模型指導方針,而不是安全性行為。 雖然連結需求是用來檢查是否符合程序設計模型需求, HostProtectionAttribute 但不是安全性許可權。

如果主機沒有程序設計模型需求,則不會發生連結需求。

此屬性會識別下列各項:

  • 不符合主程式設計模型,但其他方面無害的方法或類別。

  • 不符合主機程式設計模型的方法或類別,可能會導致伺服器管理的使用者程式代碼不穩定。

  • 不符合主機程序設計模型的方法或類別,可能會導致伺服器進程本身的不穩定。

備註

如果您要建立可由可在受主機保護環境中執行的應用程式所呼叫的類別庫,您應該將此屬性套用至公開 HostProtectionResource 資源類別的成員。 具有此屬性的 .NET Framework 類別庫成員只會檢查立即呼叫端。 您的程式庫函數也必須以相同方式檢查其直接呼叫者。

請在HostProtectionAttribute了解更多有關 HPA 的資訊。

程式代碼分析規則

針對 SQL Server,用來引進同步處理或線程處理的所有方法都必須使用 HPA 來識別。 這包括共享狀態、同步處理或管理外部進程的方法。 HostProtectionResource影響 SQL Server 的值是SharedStateSynchronizationExternalProcessMgmt。 不過,任何公開任何 HostProtectionResource 的方法都應該由 HPA 識別,而不只是使用影響 SQL 的資源。

請勿在非管理程式代碼中無限期阻塞

在 Unmanaged 程式代碼中封鎖,而不是在 Managed 程式代碼中封鎖可能會導致拒絕服務攻擊,因為 CLR 無法中止線程。 封鎖的線程可防止 CLR 卸除 AppDomain,至少不需要執行一些極其不安全的作業。 使用 Windows 同步處理基本類型進行封鎖是我們無法允許的明確範例。 如果可能的話,應該避免在呼叫 ReadFile 時阻塞套接字,理想情況下,Windows API 應該提供一種機制,讓此類操作能夠逾時。

任何呼叫原生的方法都應該使用 Win32 呼叫搭配合理的有限逾時。 如果允許使用者指定逾時,則不允許使用者指定無限逾時,而不需要特定安全性許可權。 作為指導方針,如果一個方法會封鎖超過約10秒,您需要使用支援逾時的版本,或者需要額外的CLR支援。

以下是一些有問題的 API 範例。 管道(匿名和具名)可以建立超時,不過,程式代碼必須確保它永遠不會呼叫 CreateNamedPipeWaitNamedPipe 使用 NMPWAIT_WAIT_FOREVER。 此外,即使已指定逾時,仍可能會有非預期的封鎖。 在匿名管道上呼叫 WriteFile 會封鎖直到寫入所有位元組為止,這表示如果緩衝區中有未讀取的數據, WriteFile 呼叫將會封鎖,直到讀取器釋放管道緩衝區中的空間為止。 套接字應該一律使用具備逾時機制的 API。

程式代碼分析規則

在非受控程式碼中阻塞而不設定逾時是一種阻斷服務攻擊。 請勿對 WaitForSingleObjectWaitForSingleObjectExWaitForMultipleObjectsMsgWaitForMultipleObjectsMsgWaitForMultipleObjectsEx 執行平台呼叫。 請勿使用NMPWAIT_WAIT_FOREVER。

識別任何 STA-Dependent 特徵

識別任何使用 COM 單執行緒單元 (STA) 的程式碼。 SQL Server 程式中會停用 STA。 相依於 CoInitialize的功能,例如性能計數器或剪貼簿,必須在 SQL Server 內停用。

確保終結器沒有同步處理問題

.NET Framework 的未來版本可能會有多個完成項線程,這表示相同類型之不同實例的完成項會同時執行。 它們不必完全是線程安全的,因為垃圾收集器保證只有一個線程會對指定的物件實例執行終結器。 不過,終結器必須編寫程式碼,以避免在多個不同物件實例上同時運行時發生競爭條件和死結。 當在完成項中使用任何外部狀態時,例如寫入記錄檔,必須處理執行緒問題。 請勿依賴最終處理來提供線程安全性。 請勿使用受管理或原生的線程本機存儲來將狀態儲存在析構函數執行緒上。

程式代碼分析規則

終結器必須沒有同步處理問題。 請勿在終結器中使用靜態可變狀態。

盡可能避免非受控記憶體

非受控記憶體可能會洩漏,就像作業系統控制代碼一樣。 可能的話,請嘗試使用 stackalloc 或固定的 Managed 物件在堆疊上使用記憶體,例如 固定語句 或使用 GCHandle byte[]。 GC最終會清理掉這些。 不過,如果您必須配置 unmanaged 記憶體,請考慮使用衍生自 SafeHandle 的類別來封裝記憶體分配。

請注意,至少有一個情況適用 SafeHandle 是不夠的。 對於配置或釋放記憶體的 COM 方法呼叫,一個 DLL 通常會透過 CoTaskMemAlloc 配置記憶體,然後另一個 DLL 會使用 CoTaskMemFree 釋放該記憶體。 在這些位置使用 SafeHandle 會不適當,因為它會嘗試將 Unmanaged 記憶體的存留期系結至 的存留期 SafeHandle ,而不是允許其他 DLL 控制記憶體的存留期。

檢查所有 catch(Exception) 的用法

捕捉所有例外狀況而非特定例外狀況的區塊,現在也會捕捉異步例外狀況。 檢查每個 catch(Exception)區塊,尋找重要資源釋放或回滾程式碼可能被略過的情況,以及 catch 區塊中在處理 ThreadAbortExceptionStackOverflowExceptionOutOfMemoryException時可能出現的不正確行為。 請注意,此程式代碼可能是記錄或進行一些假設,表示它可能只會看到特定例外狀況,或每當發生例外狀況時,它就只會因為一個特定原因而失敗。 您可能需要更新這些假設,才能包含 ThreadAbortException

請考慮變更所有攔截所有例外狀況的位置,以攔截您預期會擲回的特定例外狀況類型,例如 FormatException 來自字串格式方法的 。 這可防止 catch 區塊在非預期的例外狀況上執行,並有助於確保程式代碼不會藉由攔截非預期的例外狀況來隱藏 Bug。 一般來說,永遠不要在函式庫代碼中處理例外狀況(需要您處理例外狀況的代碼可能表示您呼叫的代碼中有設計缺陷)。 在某些情況下,您可能會想要攔截例外並拋出不同類型的例外,以提供更多數據。 在此情況下,請使用巢狀例外狀況,將失敗的真正原因儲存在新 InnerException 例外狀況的 屬性中。

程式代碼分析規則

檢閱所有受控代碼中的 catch 區塊,以捕捉所有物件或所有例外。 在 C# 中,這表示標記catch{}和catch(Exception){}。 請考慮將例外狀況類型設為非常特定,或檢閱程式代碼,以確保在攔截非預期的例外狀況類型時不會以不良方式運作。

請勿假設受控線程是 Win32 線程 – 它是 Fiber

使用受控線程本機記憶體可以運作,但您可能不會使用非受控線程本機記憶體,或假設代碼會在目前的作業系統的線程上再次執行。 請勿變更線程地區設定之類的設定。 請勿透過平臺叫用 InitializeCriticalSectionCreateMutex,因為它們需要進入鎖定的那條操作系統執行緒也必須退出鎖定。 由於使用 Fiber 時不會發生這種情況,因此無法直接在 SQL 中使用 Win32 重要區段和 Mutex。 請注意,Managed Mutex 類別不會處理這些線程親和性考慮。

您可以放心地在 Thread 物件的受控環境上使用大部分的狀態,包括受控執行緒的區域儲存和執行緒當前的 UI 文化設定。 您也可以使用 ThreadStaticAttribute,讓現有靜態變數的值只能由目前的 Managed 線程存取(這是在 CLR 中執行光纖本機記憶體的另一種方式)。 基於程式設計模型的原因,您無法在 SQL 中執行時變更線程目前的文化特性。

程式代碼分析規則

SQL Server 以光纖模式執行;請勿使用線程本機記憶體。 避免平台調用呼叫 TlsAllocTlsFreeTlsGetValueTlsSetValue.

讓 SQL Server 處理使用者模擬

由於模擬會在線程層級上運作,而且 SQL 可以在 Fiber 模式中執行,所以 Managed 程式代碼不應該模擬使用者,而且不應該呼叫 RevertToSelf

程式代碼分析規則

讓 SQL Server 處理模擬。 請勿使用 RevertToSelfImpersonateAnonymousTokenDdeImpersonateClientImpersonateDdeClientWindowImpersonateLoggedOnUserImpersonateNamedPipeClientImpersonateSelfRpcImpersonateClientRpcRevertToSelfRpcRevertToSelfExSetThreadToken

請勿呼叫 Thread::Suspend

暫停線程的能力可能會顯示為簡單的作業,但可能會導致死結。 如果持有鎖的線程被第二個線程中斷暫停,然後第二個線程嘗試獲取相同的鎖,就會發生死鎖。 Suspend 目前可能會干擾安全性、類別載入、遠端處理和反射。

程式代碼分析規則

不要呼叫 Suspend。 請考慮改用實際的同步處理基本類型,例如 SemaphoreManualResetEvent

使用受限的執行區域和可靠性合約保護重要作業

執行更新共享狀態或需要確定性完全成功或完全失敗的複雜作業時,請確定它受到限制執行區域 (CER) 的保護。 這可確保程式碼會在每種情況下執行,即使是執行緒突然中止或意外的 AppDomain 卸載。

CER 是在呼叫 try/finally之後緊接出現的特定PrepareConstrainedRegions區塊。

這樣做會指示 Just-in-time 編譯器在執行 try 區塊之前,先準備 finally 區塊中的所有程式碼。 這可確保最後一個區塊中的程式代碼已建置,而且在所有情況下都會執行。 CER 中具有空白 try 區塊並不罕見。 使用 CER 可防範異步線程中止和記憶體不足例外狀況。 如需能額外處理因程式碼過深導致堆疊溢出的 CER 形式,請參閱 ExecuteCodeWithGuaranteedCleanup

另請參閱