Excel 中的多線程重新計算
適用於:Excel 2013 |Office 2013 |Visualstudio
Microsoft Office Excel 2007 是第一個使用多線程重新計算 (MTR) 工作表的 Excel 版本。 您可以將 Excel 設定為在重新計算時使用最多 1024 個並行線程,而不論電腦上的處理器或處理器核心數目為何。
注意事項
每個線程都有相關聯的操作系統額外負荷,因此您不應該將Excel設定為使用比您需要更多的線程。
如果計算機有多個處理器或處理器核心,操作系統會以最有效率的方式負責將線程配置給處理器。
Excel MTR 概觀
Excel 會嘗試識別可在不同線程上同時重新計算的計算鏈結部分。 下列非常簡單的樹狀結構 (其中 x ← y 表示 y 只相依於 x) 會顯示此範例。
圖 1: Calculating concurrently on different threads
在
計算 A1 之後,就可以在一個線程上計算 A2 和 A3,而 B1 和 C1 則可以在另一個線程上計算,前提是所有單元格都是安全線程。
注意事項
安全線程數據格一詞表示僅包含安全線程函式的儲存格。 什麼是和不是安全線程是 Excel 所詳述的 「什麼是線程安全」和「不被視為安全線程」。
相較於此範例,最實用的活頁簿包含更複雜的相依性樹狀結構。 此外,在計算完成之前,無法得知單元格的重新計算時間,而且可能會因為函式的自變數而有很大的差異。 為了取得最佳結果,Excel 會嘗試改善每個計算的計算順序,直到無法進一步優化為止。
Excel 會使用單一主線程來執行或執行下列專案:
內建命令
XLL 命令
XLL 載入宏管理員介面函式 (xlAutoOpen 函式等)
Microsoft Visual Basic for Applications (VBA) 使用者定義命令 (通常稱為宏)
VBA 用戶定義函數
內建的線程不安全工作表函式 (請參閱下一節以取得清單)
XLM 宏表用戶定義的命令和函式
COM 載入宏命令和函式
條件式格式化運算式內的函式和運算元
工作表公式中所使用之定義名稱定義內的函式和運算符
使用 F9 鍵在公式編輯方塊中強制評估表達式
除非 Excel 設定為使用一個以上的線程,否則所有工作表公式都會在主線程上評估,而不論函式是否為線程安全。 當使用者指定應該使用多個線程時,其他線程會用於安全線程的儲存格。 請注意,從負載平衡的觀點來看,主線程仍可用於安全線程數據格。
值得一提的是,Excel 不會一次執行多個命令,因此您不需要採用與撰寫安全線程函式相同的預防措施,例如使用線程本機記憶體和重要區段。
Excel 認為安全線程的內容和內容
Excel 只會將下列專案視為安全線程:
Excel 中的所有一元和二進位運算符。
幾乎所有從 Excel 2007 開始的內建工作表函式 (查看例外狀況清單)
已明確註冊為安全線程的 XLL 載入宏函式。
不是安全線程的內建工作表函式如下:
語音
使用 「format」 或 「address」 自變數時的 CELL
INDIRECT
GETPIVOTDATA
CUBEMEMBER
CUBEVALUE
CUBEMEMBERPROPERTY
CUBESET
CUBERANKEDMEMBER
CUBEKPIMEMBER
CUBESETCOUNT
提供 sheet_name) (第五個參數的位址
參考數據透視表的任何資料庫函 (DSUM、 DAVERAGE 等)
錯誤。類型
連結
為了明確起算,下列項目被視為不安全:
VBA 用戶定義函數
COM 載入宏使用者定義函數
XLM 宏表用戶定義函數
XLL 載入宏函式未明確註冊為安全線程
其含意是下列作業和函式不是安全線程,如果是從註冊為安全線程的 XLL 函式呼叫,則會失敗:
呼叫 XLM 資訊函式,例如 xlfGetCell (GET。CELL) 。
呼叫 xlfSetName (SET.NAME) 定義或刪除 XLL 內部名稱。
使用 xlUDF 呼叫線程不安全的使用者定義函式。
針對包含線程不安全函式的表達式呼叫 xlfEvaluate 函式,或包含定義其定義包含線程不安全函式之定義名稱的運算式。
呼叫 xlAbort 函式以清除中斷條件。
呼叫 xlCoerce 函式以取得未計算儲存格參考的值。
注意事項
XLL 工作表函式不允許呼叫 C API 命令,例如 xlcSave,不論它們是否已註冊為安全線程。
假設宣告為安全線程的 XLL 函式無法呼叫 XLM 資訊函式或參考未計算的數據格,Excel 不允許註冊為宏工作表對等專案的 XLL 函式也註冊為安全線程。 因此,嘗試使用 xlCoerce 取得未計算儲存格參考的值會失敗,並出現 xlretUncalced 錯誤。 呼叫 XLM 資訊函式失敗,並出現 xlretFailed 錯誤。 先前列出的其他數據點會失敗,並出現 Excel C API 中引進的錯誤碼: xlretNotThreadSafe。
僅限 C API 的回呼函式全都具備線程安全:
xlCoerce (,但未計算儲存格參考的強制型轉失敗)
xlFree
xlStack
xlSheetId
xlSheetNm
xlAbort (,除非用來清除中斷條件)
xlGetInst
xlGetHwnd
xlGetBinaryName
xlDefineBinaryName
其中一個例外是 xlSet 函式,在任何情況下都是命令對等專案,因此無法從任何工作表函式呼叫。
XLL 工作表函式可以向 Excel 註冊為安全線程。 這會告訴 Excel 可以安全地同時在多個線程上呼叫函式,不過您必須確定這真的是這種情況。 如果註冊為安全線程的函式行為不安全,則可能會使 Excel 不穩定。
將 XLL 函式註冊為安全線程
開發人員在撰寫安全線程函式時必須遵守的規則如下:
請勿在其他可能不安全線程的 DLL 中呼叫資源。
請勿透過 C API 或 COM 進行任何不安全的線程呼叫。
使用重要區段保護多個線程可以同時使用的資源。
針對線程特定記憶體使用線程區域記憶體,並將函式內的靜態變數取代為線程局部變數。
Excel 會施加額外的限制:安全線程函式無法註冊為宏表對等專案,因此無法呼叫 XLM 資訊函式或取得未重新計算單元格的值。
記憶體爭用
多線程系統必須解決兩個基本問題:
如何保護必須由多個線程讀取或寫入的記憶體。
如何建立和存取與執行中線程相關聯且私用的記憶體。
Windows 作業系統和 Windows 軟體開發套件 (SDK) 分別提供下列兩者的工具:重要區段和線程本機記憶體 (TLS) API。 如需詳細資訊,請參閱 Excel 中的記憶體管理。
例如,當兩個工作表函式 (或兩個同時執行相同函式的實例時,) 需要存取或修改 DLL 專案中的全域變數時,就會發生第一個問題。 請記住,此類全域變數可能會隱藏在類別物件的全域可存取實例中。
例如,當工作表函式在函式主體程式代碼內宣告靜態變數或物件時,可能會發生第二個問題。 C/C++ 編譯程式只會建立所有線程使用的單一複本。 這表示函式的一個實例可能會變更值,而不同線程上的另一個實例可能假設值是先前設定的值。
MTR 的範例應用程式
任何導出工作表函式的 XLL 都可以利用 Excel 中 MTR) (多線程重新計算,前提是這些函式不需要執行線程不安全的動作。 這可讓 Excel 儘快重新計算相依於活頁簿的活頁簿,因此無論應用程式為何,都可加以調整。
具體而言,MTR 會對活頁簿的重新計算時間產生極大的影響,活頁簿會呼叫使用者定義函式 (UDF) 本身會呼叫外部進程來取得所需的結果。 特別是,請考慮呼叫可同時處理許多要求的遠端伺服器的 UDF,以及包含該函式之許多呼叫的活頁簿。 如果活頁簿的重新計算是單個線程,則每次對 UDF 的呼叫,以及遠端伺服器的呼叫,都必須先完成,才能進行下一個呼叫。 這會浪費伺服器一次處理許多呼叫的能力。 如果活頁簿的重新計算是多線程的,Excel 可以同時或快速地連續進行多次呼叫。
如果 Excel 設定為使用與伺服器相同的線程數目—呼叫它 N,而且活頁簿的相依性樹狀結構拓撲允許它,則重新計算總時間可能會減少為接近單個線程計算時間的 1/N。 即使活頁簿執行所在的用戶端電腦 () 只有一個處理器,尤其是呼叫伺服器所花費的時間相對於伺服器處理呼叫所花費的時間較小時,也可能成立。
每個額外的線程都有操作系統額外負荷。 因此,指定的活頁簿和指定的伺服器和用戶端計算機可能需要進行一些實驗,才能找出應該告知使用 Excel 的最佳線程數目。
例如,請考慮執行 Excel 的單一處理器電腦,以及包含 1,000 個儲存格的活頁簿。 它會呼叫 UDF,而 UDF 會接著呼叫一或多個遠端伺服器。 假設 1,000 個儲存格彼此之間沒有相依性,因此 Excel 不需要等候一個呼叫完成,再呼叫下一個。 (此條件約束的某些缺點可能不影響此範例。) 如果伺服器可以同時處理 100 個要求,而且 Excel 設定為使用 100 個線程,則運行時間可以縮減為僅使用一個線程的 1/100。 與 Excel 配置對每個線程的呼叫以及管理 100 個線程的作業系統相關聯的額外負荷,表示實際上減少的不是那麼好。 這裡也有一個隱含假設,即伺服器調整良好,並要求它同時處理100個工作,並不會大幅影響個別工作完成時間。
這項技術可以具有重要優點的一個實際應用程式,就是 Monte-Carlo 方法,以及其他可分割成較小子工作的數值密集型工作,這些子工作可以伺服數位到伺服器。
Excel Services 考慮
Excel Services 支援在伺服器上載入、計算及轉譯 Excel 電子表格。 用戶接著可以使用標準瀏覽器工具來存取電子錶格並與之互動。
Excel Services UDF 是使用 Microsoft .NET Framework Managed 程式代碼建立,並透過 .NET 元件提供。 Excel Services 不支援 XLL。 Managed 程式代碼伺服器 UDF 資源可以呼叫 XLL 來存取其功能,讓使用者可以具有與用戶端載入活頁簿相同的伺服器載入活頁簿功能。
若要以這種方式提供 XLL 的函式,它們必須包裝在 .NET 元件中,以將自變數和傳回值從原生數據類型轉換為 .NET Framework 受控數據類型,並呼叫 XLL 函式。 .NET 包裝函式會針對每個要存取的 XLL 函式匯出一個伺服器 UDF。 另一個需求是,以這種方式呼叫的任何 XLL 函式都必須是安全線程。 因為 XLL 函式未以用戶端 Excel 的註冊方式註冊,所以伺服器和 .NET 包裝函式無法強制執行安全線程。 XLL 開發人員必須負責確保這一點。