Excel 2007 中的多線程重計算


Excel 2007 是第一個使用了多線程工作表函數重計算的版本。最多你可以設置使用 1024 個並發線程進行重計算。而不用考慮計算機上的 CPU數量 或 CPU核心數量。

注意: 操作系統系統開銷與線程數有着密切的關系,因此你不要把線程數量設置過多。

如果計算機使用了多個處理器或是多核處理器,操作系統負責以最有效的方式分配線程。

Excel 2007 MTR 概要

Excel 會嘗試在不同的線程上同時對計算鏈進行計算。下圖是一個十分簡單的計算鏈。

假設所有單元格中的函數都是線程安全的條件下。當 A1 計算完后,A2 然后 A3 在另一個線程上完成計算,B1 然后 C1也會在一個不同的線程上完成計算。


注釋 線程安全單元格(thread-safe cell) 表示單元格中只包含了線程安全函數。


通常 workbook 包含了比以上實例復雜的多的關系樹。而且,單元格的重計算時間,在計算完成以前是無法知道的,同時函數計算時間與它的參數有着密切的關系。為了提升計算的效率,Excel 會嘗試在每次計算時先優化計算的順序,直到無法繼續優化為止。

Excel 2007 使用單個的主線程執行以下操作:

  • 運行 Excel 內部命令
  • 運行 XLL 命令
  • 運行 XLL Add-in 管理接口函數 (如:xlAutoOpen 函數,等)
  • 運行 VBA 中的用戶定義命令(也就是宏命令)。
  • 運用 VBA 用戶定義函數。
  • 不是線程安全的工作表函數。
  • COM add-in 命令和函數。
  • 條件格式表達式中的函數和操作
  • 用於公式中定義的名稱定義的函數和操作
  • 在公式編輯框中使用 F9 鍵強制評估一個表達式。

所有工作表公式,無論是否是線程安全的,都是在主線程上進行評估操作,除非 Excel 2007 中設置了使用多個線程處理。如果用戶設置了使用多個線程,那么添加的線程將會用於線程安全單元格。注意此時主線程出於負載平衡方面的考慮,仍然會用於線程安全單元格。

這兒需要重申的是,Excel 一次只能運行一個命令 ( command 而不是 function )。因此對 command 來說,你不用考慮創建線程安全函數時的那些防范措施。如,線程局部內存和臨界段。

Excel 2007 線程安全性考慮

Excel 2007 只需要在以下幾種情況中考慮線程安全性:

  • Excel中的一元操作和位操作。
  • Excel 2007 中幾乎所有的內置工作表函數。
  • XLL add-in 明確被注冊為線程安全的函數。

下面的 Excel 2007 內置函數是非線程安全:

  • PHONETIC
  • CELL 當 format 或 address 參數被使用時。
  • INDIRECT
  • GETPIVOTDATA
  • CUBEMEMBER
  • CUBEVALUE
  • CUBEMEMBERPROPERTY
  • CUBESET
  • CUBERANKEDMEMBER
  • CUBEKPIMEMBER
  • CUBESETCOUNT
  • ADDRESS 第50個參數給了任何一個數據庫函數(DSUM、DAVERAGE等等),引入到了關鍵表
  • ERROR.TYPE
  • HYPERLINK

以下幾種情況已經明確了不是線程安全的。

  • VBA 用戶定義函數
  • COM add-in 用戶定義函數
  • XLM 宏表用戶定義函數
  • XLL add-in 沒有被注冊為線程安全的函數

下面的操作和函數不是線程安全的,如果調用一個注冊為線程安全的 XLL 函數,會提示操作失敗。

  • 調用 XLM 信息函數,如果 xlfGetCell ( GET.CELL )
  • 調用 xlfSetName ( SET.NAME ) 定義和刪除 XLM 內部名稱
  • 使用 xlUDF 調用非線程安全的用戶定義函數。
  • 為包含了非線程安全函數或是包含了非線程安全函數的定義名稱使用 xlfEvaluate 函數。
  • 使用 xlAbort 函數清除一個中斷條件。
  • 調用 xlCoerce 函數獲取一個非計算單元格引用值。

注意: XLL 工作表函數不允許調用 C-API 命令,例如,xlcSave、不管它是否被注冊為線程安全的。


考慮到 XLL 函數聲明為線程安全的,不能調用 XLM 信息函數或引用非計算單元格,Excel 2007 不允許 XLL 函數注冊為宏表等效線程安全函數。因此,打算使用 xlCoerce 獲取非計算單元格引用,會失敗返回 xlretUncalced 錯誤。調用 XLM 信息函數會返回 xlretFailed 錯誤。其它錯誤點,參考先前在 Excel 2007 C API ,xlretNotThreadSafe 中介紹的錯誤代碼。

以下的 只用於 XLL中的 C API 回調函數都是線程安全的

  • xlCoerce (except although coercion of uncalculated cell references fails)
  • xlFree
  • xlStack
  • xlSheetId
  • xlSheetNm
  • xlAbort (except when used to clear a break condition)
  • xlGetInst
  • xlGetHwnd
  • xlGetBinaryName
  • xlDefineBinaryName

唯一的例外是 xlSet 函數,它在任何情況下,都不能從任何工作表函數中調用。

所有的 XLL工作表函數,都可以注冊為 Excel 線程安全函數。也就是說 Excel 可以同時在多個線程上安全的使用它們。如果函數不是線程安全的,但你又注冊為線程安全的,就會影響到 Excel 的穩定性。

將 XLL 函數注冊為線程安全的。

編寫線程安全函數,開發者必需遵守以下規則:

  • 不要調用其它 DLL 中的資源,這些資源可以是非線程安全的。
  • 不要通過 C API 或 COM 做任何不安全的線程調用。
  • 使用臨界段保護多線程訪問下的資源。
  • 使用線程專有的本地內存,來代替靜態變量。

Excel 強加了額外的限制: 線程安全函數不能注冊為函數表函數,因此不能調用 XLM 信息函數 或 非計算單元格的值。

內存爭用

多線程系統必需解決兩個基本問題:

  • 如何在多個線程讀寫內存時,保護內存。
  • 如何創建和訪問線程的私有內存。

Windows 操作系統和 Windows 開發包(SDK ),為解決以上問題,分別提供了兩個工具:臨界段 和 線程本地存儲(TLS) API。

當兩個工作表函數,需要訪問和編輯一個DLL中全局變量,第一個問題就會出現,大家要記住的是,這樣一個全局變量,可能隱藏在全局的類實例中。

當一個工作表函數在函數體內代碼聲明了一個靜態變量或對象。C/C++ 編譯器將只為所有的線程創建一個內存空間。這個意思簡單來說,就是多個不同的線程將會爭用同一個變量內存。並隨意對同一個變量內容進行修改。

MTR 的實例應用

任何 XLL 輸出的函數,都可以使用 Excel 2007 中的多線程重計算技術,這些函數不需要執行線程不安全操作。 這能讓 Excel 快速重計算工作簿。

特別是,MTR 會極大縮減工作表中 UDF 函數的重計算時間。尤其是,考慮到 UDF 調用遠程服務,它會同時處理多個請求,並且這個 函數 關聯了很多單元格。如果是單線程的重計算,每次調用 UDF,訪問 遠程服務,必需等前一個調用完成操作后,才能執行這一次操作。這浪費了許多的處理能力。如果重計算使用的多線程,Excel 可在同一時間執行多次調用。

如果 Excel 配置使用與N個線程,並且工作表的依賴樹允許使用這N個純種,那么重計算時間理論上可以減少到 單線程計算時間的 1/N 。即使計算機是單處理器的,這也是有可能的,

每個附加的線程都是增加操作系統的開銷,因此,需要進行一些測試,找到合適的線程數量。

例如,單線程處理器計算機,正在運行 Excel 2007,工作簿包含了 1000 個單元格。Excel 使用了UDF,調用了一個或多個遠程服務。假設這 100個單元格都是各自獨立的,相互之間沒有依賴性,因此 Excel 在調用下下服務之前,不需要等着前一個調用完成。如果一個服務可以同時處理100個請求,Excel 配置使用100個線程,執行時間會減少到僅僅只有單線程處理時間的 1/100 ,系統總的開銷關系着 Excel 分配的每個線程。這意味着操作系統管理 100 個線程,在實踐中減少的計算次數,並沒有如此之多。這還有一個隱含的服務尺度假設,讓它處理100個並發任務不會顯著影響單個任務完成時間。

在程序開發中,這種技術對 蒙特卡羅方法, 擁有十分重要的意義,在其它的一些數值密集型任務中,可以拆成很多子任務,發送給服務器處理。

Excel 服務注意事項

Excel 服務是 Office 2007 新發布的服務技術。它支持在服務器上,載入、計算、渲染 Excel 電子表格。然后用戶可以使用標准的 瀏覽器工具查看表格以及與它進行交互。

Excel 服務 UDF 使用 .NET 技術創建。XLL 不支持 Excel 服務。托管代碼 UDF 資源 可以訪問所有 XLL 函數,因此,用戶可以使用於客戶端工作簿一樣的函數。

通過這種方法,我們就可使用 XLL 函數,因此它們必需將參數和返回值的數據類型從本地類型轉換為 .NET 類型。NET 會為每個 XLL 函數的訪問輸出一個 UDF 服務。另外還要求這種方式調用的 XLL 函數必需是線程安全的。因為 XLL 函數並不是使用 本地 Excel 的注冊方式來注冊 XLL。NET 沒有辦法確定線程的安全性。這是 XLL 開發人員必需解決的問題 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM