看著「剩餘1秒」,已經過了30秒。以為終於快結束了,顯示卻又變成「剩餘2分鐘」。
複製檔案、安裝應用程式、匯出影片時,進度列很有用,但有時它彷彿和時鐘活在不同的世界裡。
事實上,進度列不是時鐘。進度百分比表示「已經完成多少」,剩餘時間預測「接下來大概要多久」,完成狀態則表示「必要的處理是否成功」。 把這三件事分開,就能換個角度理解「明明99%了,怎麼還沒結束」。
本文參考Windows API與UI文件,說明一般原理,並不是對某個版本檔案總管內部演算法的逆向分析。文中的數值範例和比較示範都是用來說明的虛擬工作,不是電腦或網路的實測結果。
1. 先弄清楚:究竟是什麼的100%?
假設要審閱100份文件。前99份都是簡短便條,最後一份卻是厚厚的合約,那麼「已完成99份」可以完全正確,但不代表「所需時間也已經過了99%」。
進度顯示也是如此:分母不同,意義就不同。
| 衡量依據 | 50%表示什麼 | 光看這個數字無法知道什麼 |
|---|---|---|
| 檔案數量 | 已處理目標檔案的一半 | 剩餘檔案的大小與處理時間 |
| 資料量 | 已處理目標位元組數的一半 | 後續速度,以及傳輸以外的階段 |
| 階段權重 | 已完成預設權重的一半 | 權重是否符合這次的實際耗時 |
例如,Windows的CopyFileEx使用的進度回呼會提供檔案總位元組數與已傳輸位元組數。這些是工作量資訊,並不直接提供未來還需要多少秒。1
flowchart TB
accTitle: 進度百分比、剩餘時間與完成狀態的差異
accDescr: 區分依已完成量計算的比例、基於假定速度預測的剩餘時間,以及由成功結果確認的完成狀態。
A["已完成量與總量"] --> B["進度百分比"]
C["剩餘量與預測速度"] --> D["預估剩餘時間"]
E["必要的處理成功"] --> F["整個工作完成"]
圖1:比例、預測與結果,並不是同一資訊的三種說法。
本文把可測量工作量的完成比例稱為進度百分比,顯示它的長條控制項稱為進度列,對還需多少秒的預測稱為剩餘時間估算。無法提供可靠預測,不代表已經量到的進度也變得未知。
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 9 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
2. 「剩餘10秒」如何變成「剩餘79秒」?
最簡單的剩餘時間估算公式如下:
剩餘時間 ≈ 剩餘工作量 ÷ 對未來處理速度的估計
困難的不是除法,而是未來的速度還沒有被觀測到。因此,我們用過去的速度做預測。Microsoft的Raymond Chen在2004年解釋檔案複製時間估算時,也談過預測未來的困難。那篇文章不是目前Windows所用計算公式的規格說明。2
設想一個總量為1,000 MiB的虛擬複製工作。1 MiB等於1,048,576位元組。假設工作先以80 MiB/s的固定速度執行2.5秒,接著1秒的速度降到10 MiB/s。
| 觀測時點 | 已傳輸 | 剩餘 | 用於估算的速度 | 剩餘時間 |
|---|---|---|---|---|
| 開始後2.5秒 | 200 MiB | 800 MiB | 80 MiB/s | 800 ÷ 80 = 10秒 |
| 開始後3.5秒 | 210 MiB | 790 MiB | 10 MiB/s | 790 ÷ 10 = 79秒 |
在這一秒裡,工作確實往前推進了。然而,完成剩餘工作的預測速度變成原來的八分之一,因此預估耗時反而增加。這個範例直接採用最近一個觀測區間的速度。
flowchart TB
accTitle: 工作在推進,剩餘時間為何還會增加
accDescr: 如果預測速度下降的影響超過剩餘工作量減少的影響,計算出的剩餘時間就會增加。
A["1秒內完成10 MiB"] --> B["剩餘量減少"]
C["預測速度大幅下降"] --> D["剩餘時間可能增加"]
B --> D
圖2:預估剩餘時間增加,與工作實際倒退是兩回事。
「剩餘1秒」也是同樣的道理。依剛才的速度只需1秒的工作量,如果接下來的處理較慢,就無法在1秒內結束。觀測間隔和秒數的取整方式也會影響顯示。不過,數字始終不變時,也不能直接認定為正常;還需要依後文的方法,區分處理階段、畫面更新與實際停滯。
3. 取平均值就能算準嗎?
如果把每次短暫的速度變化都反映出來,數字就會上下跳動。反過來,如果只採用從開始到現在的平均速度,最初很快的那一段就會持續影響結果,讓預測遲遲無法適應後來的持續減速。
從觀測值的組合方式就能理解這種差異。假設把過去的速度80與最新速度10,各以一半權重混合,得到的預測速度是45。與只使用最新值10相比,預測會平順一些;但如果實際速度一直維持在10,它就會在一段時間內過於樂觀。顯示平順與及時跟上變化,是不同的目標。
flowchart TB
accTitle: 平滑速度估計時的取捨
accDescr: 提高最新觀測值的權重會讓估計更敏感但也更容易波動,提高歷史值的權重則較平順但跟隨變化較慢。
A["速度觀測值"] --> B["提高最新值的權重"]
A --> C["提高歷史值的權重"]
B --> D["反應敏感但容易波動"]
C --> E["顯示平順但跟隨較慢"]
圖3:數字看起來穩定,不等於預測準確。
這不是某個產品的實作說明,而是協助思考預測方法的例子。本文的設計建議是:剛開始或剛切換階段時,不要勉強顯示秒數;有足夠觀測後,再以「大約1分鐘」等形式提供估計。與其維持沒有依據的「剩餘1秒」,不如明確說明正在重新估算,減少誤解。
4. 已完成99個檔案,資料量卻只完成9.9%
這次問題不在速度,而在計數單位。假設有100個檔案,前99個各為1 MiB,最後一個為901 MiB,總量是1,000 MiB。
前99個檔案完成時,依數量計算是99 ÷ 100 = 99%,依資料量計算卻是99 ÷ 1,000 = 9.9%。雖然只剩一個檔案,仍有90.1%的資料尚未傳輸。兩種計算都正確,只是衡量的對象不同。
flowchart TB
accTitle: 最後一個檔案特別大時的進度
accDescr: 處理完99個小檔案後,如果還剩一個大檔案,依檔案數量與位元組數計算的進度就會相差很大。
A["99個小檔案"] --> B["依數量看幾乎完成"]
C["最後還剩一個大檔案"] --> D["仍有大量資料未傳輸"]
B --> E["同一工作的不同顯示"]
D --> E
圖4:依檔案數量達到99%,並不保證只剩1%的時間。
那麼,只看位元組數就夠了嗎?也不是。透過SMB傳輸大量小檔案時,會反覆產生建立檔案與要求往返的額外負擔。即使總位元組數與一個大檔案相同,耗時也不一定相同。3
所以,「還剩500 MiB」可以是準確的測量結果,但這500 MiB由什麼組成,仍會影響所需時間。同時顯示檔案數量與位元組數的價值,不是其中一個數字有錯,而是它們能補足彼此未呈現的資訊。
5. 「準備中」很久,有時是在尋找分母
計算百分比需要作為分母的總量。但剛下達「處理整個資料夾」的指令時,程式不一定已經列舉完裡面所有層級的檔案。
假設程式認為共有100項,處理了80項後又發現100項。同樣的80項就從80/100變成80/200。顯示從80%下降到40%,但已完成的工作沒有消失。把尚未確定的總量當作確定值呈現,才造成了與讀者預期的落差。
flowchart TB
accTitle: 總量尚未確定時如何顯示進度
accDescr: 尋找處理對象時不顯示確定的比例,等總量已知後,再搭配已完成量計算百分比。
A["尋找處理對象"] --> B{"總量已知了嗎"}
B -->|"還沒有"| C["顯示階段與已發現數量"]
B -->|"已知"| D["顯示百分比"]
圖5:沒有分母,與進度為0%是不同的狀態。
Windows的進度控制項也區分確定與不確定進度;後者表示仍在處理中,而不提供確定數值。4 對於這裡的情況,本文建議在列舉時顯示「正在尋找對象:已發現1,200項」,等總量確定後再顯示比例。沒有剩餘秒數,本身並不能證明程式什麼都沒做。
6. 「傳輸100%」與「全部結束」不是同一個邊界
6.1 最後可能還有別的工作
為了說明問題,把一個應用程式的工作分成「傳輸 → 驗證 → 確定最終結果」三個階段。如果設計要求傳輸後驗證內容,那麼傳輸結束時,整個工作還沒完成。這只是一個應用程式設計範例,不代表所有複製或安裝都遵循這個順序。
flowchart TB
accTitle: 傳輸完成與整個工作完成
accDescr: 這個虛擬應用程式在傳輸後還需要驗證並確定最終結果,因此不能僅憑已傳輸位元組數判斷整體成功。
A["傳輸"] --> B["驗證"] --> C["確定最終結果"] --> D["整體成功"]
A -.-> E["傳輸100%只到這裡"]
圖6:明確說明完成的範圍,就能解釋為何100%後仍有其他階段。
此時,與其把代表整個工作的進度列固定在99%,一直顯示「剩餘1秒」,不如把狀態切換為「傳輸完成,正在驗證」。Microsoft的桌面UI指南也要求,不要在實際作業結束之前顯示整個工作已完成。5
6.2 寫入也不只有一個完成邊界
Windows通常會對檔案寫入使用快取。應用程式發出寫入與資料落到儲存媒體,具體邊界會因設定及API約定而異。6 FlushFileBuffers是把指定檔案的緩衝資訊傳送到裝置的API。7
flowchart TB
accTitle: 緩衝寫入的概念圖
accDescr: 使用緩衝時,應用程式提交的寫入與儲存媒體側的實際寫入,不應視為同一時刻發生的事件。
A["應用程式發出寫入"] --> B["保存在緩衝區"] --> C["寫入儲存媒體"]
圖7:這是使用緩衝時的概念圖,不是某個產品的完成條件說明。
但是,不能說「停在99%一定是在將快取寫回」。究竟是在驗證、寫回,還是等待其他作業,需要檢查應用程式設計或記錄。不要只看進度數字,就推斷可以拔掉USB裝置或切斷電源。
7. 是工作真的停了,還是只有顯示停了?
在Windows的WPF應用程式中,UI執行緒上的Dispatcher負責處理介面工作。長時間占用該執行緒,會讓畫面更新和輸入回應延遲。實際執行的工作,與把結果顯示到畫面上的工作,需要分開考慮。8
flowchart TB
accTitle: 從實際處理到進度顯示的路徑
accDescr: 即使實際工作已回報進度,如果UI端無法處理更新,變化也不會反映到畫面上。
A["實際處理"] --> B["回報進度"] --> C["UI處理更新"] --> D["反映到畫面"]
E["UI執行緒阻塞"] -.-> C
圖8:顯示停止,不一定代表實際處理也停止。
反過來,如果介面動畫與實際處理獨立運作,實際工作處於等待狀態時,圓形動畫仍然可以轉動。「在動就正常,不動就故障」這樣的二分判斷並不夠。
| 觀察什麼 | 可以取得什麼線索 | 光憑它無法確定什麼 |
|---|---|---|
| 已處理數量、資料量、階段名稱是否變化 | 已回報的工作進展 | 整個工作最後能否成功 |
| 記錄中的時間、對象與錯誤 | 記錄顯示在哪一步做什麼 | 沒有寫入記錄的處理是否停止 |
| 相關處理程序的CPU、磁碟與網路活動 | 當時的資源使用情況 | 是正常推進、等待,還是無效反覆 |
| 其他視窗中的確認提示 | 是否在等待使用者輸入 | 所有可能的停滯原因 |
本文建議先記錄畫面與開始時間,檢查是否有等待確認的提示,再比較處理數量或記錄是否變化。工作管理員裡的數值只當作輔助證據。考慮結束工作前,應先確認應用程式的取消方式,以及未完成的輸出會如何處理。
.NET的取消機制也是合作式的:發出取消要求不會立即停止處理,處理端需要回應要求。9 因此,「正在取消」與「已取消」也應該是不同狀態。僅憑進度顯示,無法得出一個人人適用的「超過幾分鐘就可以安全強制結束」的數值。
8. 比較同一工作的三種顯示方式
這個示範透過按鈕切換虛擬工作的觀測時點。不需要等待,也不會讀取、寫入或上傳檔案。第一個案例是「99個小檔案加1個大檔案」。同一時點的檔案數量進度列、傳輸量進度列和工作整體狀態會並列呈現。
傳輸最後一個大檔案的過程中,依數量計算的進度列仍停在99%,但依資料量計算的進度列會繼續增加。傳輸量達到100%時,整體狀態仍是驗證中;推進到下一個觀測時點,才會變成成功。可以由此看出,光是換一種顯示方式,同一工作就可能看起來像停住了,也可能明顯正在推進。
flowchart TB
accTitle: 如何閱讀比較示範
accDescr: 將同一虛擬工作在同一時點的觀測值,分別交給檔案數量、傳輸量和整體狀態三種顯示。
A["一個虛擬工作"] --> B["同一觀測時點"]
B --> C["檔案數量比例"]
B --> D["傳輸量比例"]
B --> E["工作整體狀態"]
圖9:示範比較的是一個工作的三種檢視方式,而不是三個不同的工作。
另一個案例重現第2節的速度下降,顯示10秒如何算成79秒。所用計算如下。這只是用來學習的簡單預測,沒有實作實際應用程式中的重試、平行處理或分階段預測。
function estimateSeconds(remaining, rate) {
if (!Number.isFinite(remaining) || remaining < 0) return null;
if (remaining === 0) return 0;
if (!Number.isFinite(rate) || rate <= 0) return null;
const seconds = remaining / rate;
return Number.isFinite(seconds) ? seconds : null;
}
傳入remaining與rate時,必須保持單位一致,例如MiB與MiB/s。這裡回傳0只表示估算範圍內已經沒有剩餘工作量,不代表應用程式的整個工作已成功。剩餘量為正且速度為0或未知時回傳null,避免把「無法估算」誤認為「剩餘0秒」。
9. 開發者應追求「不誤導」,而不只是「看起來精確」
對於本文討論的設計,我會分別保存並顯示目前階段、已測得的工作量、有依據時才提供的剩餘時間,以及成功、失敗或取消的結果。即使把各階段百分比合成一根進度列,「傳輸80%、驗證20%」也只是設計上的權重,不保證這次耗時就依這個比例分配。
flowchart TB
accTitle: 依觀測資訊建立進度介面
accDescr: 將目前階段、實際測量的數量、有依據時的剩餘時間估算,以及最終結果分開顯示。
A["工作提供的資訊"] --> B["目前階段"]
A --> C["已測量工作量與總量"]
A --> D["條件滿足時的估算"]
A --> E["成功、失敗或取消"]
圖10:在介面上也要區分觀測結果與預測。
例如,「正在驗證:400 / 1,000項,正在估算剩餘時間」即使沒有秒數也很有用。如果顯示更新時間,「進度最後增加的時間」與「介面最後成功通訊的時間」也不是同一回事。不要只更新後者,卻讓使用者誤以為實際工作一直在正常推進。
無障礙設計也是同樣的思路。Web中的progress元素可以透過省略值來表示不確定進度。10 對於自訂的ARIA進度顯示,值未知時應省略aria-valuenow,並提供能說明「是什麼的進度」的無障礙名稱。11 不只視覺顯示,螢幕閱讀器讀出的資訊也應該如實反映目前已知的範圍。
10. 常見問題
一直顯示剩餘1秒,是發生故障了嗎?
光看顯示無法判斷。要區分估算失準、其他階段、畫面更新延遲,以及工作實際停滯。「經常這樣,所以不用管」與「超過1秒了,所以一定壞了」都太過武斷。
99%是否代表只剩總時間的1%?
不是。不論比例的單位是檔案還是位元組,每個單位需要的時間都不一定固定。不能用已經過的總時間乘以1%,來求出剩餘時間。
圓形動畫還在轉,就代表正常嗎?
表示正在運作的動畫,與工作實際推進的證據是兩回事。應搭配已完成量、階段和記錄來判斷。只看動畫不能保證工作能正常完成。
不知道剩餘時間,就不能顯示進度嗎?
知道總量與已完成量,就可以顯示比例。預測不可靠時只省略秒數。如果連總量都未知,就採用不確定進度顯示,並附上階段名稱與已處理數量。
11. 總結:剩餘時間是預測,完成狀態是結果
「剩餘1秒」持續很久,不是因為電腦不會計算1秒,而是它要依已觀察到的工作量,估計還沒發生的處理時間。再加上計數單位、目標尋找、最後的階段與畫面更新延遲,情況就更複雜。
flowchart TB
accTitle: 如何解讀遲遲不結束的進度顯示
accDescr: 依序確認比例在衡量什麼、預測速度是否變化、是否還有其他階段,以及停住的是顯示還是實際處理。
A["衡量什麼的比例"] --> B["預測速度是否改變"] --> C["是否還剩其他階段"] --> D["顯示停滯還是處理停滯"]
圖11:把對數字的不滿,拆成可以查證的問題。
使用者應關注階段與變化,而不只盯著數字;開發者不要把工作量、預測和結果混在一起。比起一直猜準「還剩1秒」,更重要的是說明現在正在發生什麼,以及哪些事情仍然未知。這才是好的進度顯示該做的事。
參考資料
-
Microsoft Learn, LPPROGRESS_ROUTINE callback function. 複製進度回呼所提供位元組數的意義。 ↩
-
Microsoft, The Old New Thing, Why does the copy dialog give such horrible estimates?. 2004年的解釋,用來說明預測未來速度的困難,不是目前Windows內部實作的規格說明。 ↩
-
Microsoft Learn, Slow SMB files transfer speed. 小檔案傳輸反覆產生的檔案建立與通訊負擔。 ↩
-
Microsoft Learn, Progress controls. 確定與不確定進度的控制項。 ↩
-
Microsoft Learn, Progress Bars. 桌面應用程式進度顯示的設計指南。 ↩
-
Microsoft Learn, File Caching. 檔案快取與寫入行為。 ↩
-
Microsoft Learn, FlushFileBuffers function. 把檔案緩衝資訊傳送到裝置的API。 ↩
-
Microsoft Learn, Threading model. WPF的Dispatcher與UI執行緒回應能力。 ↩
-
Microsoft Learn, Cancellation in Managed Threads. .NET的合作式取消。 ↩
-
WHATWG, The progress element. HTML進度元素與不確定狀態。 ↩
-
W3C, WAI-ARIA 1.2: progressbar. 無障礙進度資訊的名稱與數值規則。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
同樣是 1GB,為什麼複製照片資料夾比複製一部影片還慢?
圖解 Windows 中容量相同、複製耗時卻不同的原因。整理檔案數量、SSD 與 NAS 的等待時間、打包成 ZIP 的效果、把建立·傳輸·解壓縮都算進去的比較步驟,以及 robocopy 的適用場合。
Windows 的「硬體加速 GPU 排程」是什麼?打開就會變快嗎?
以圖解方式向一般使用者說明 Windows 的硬體加速 GPU 排程(HAGS):它改變了什麼、開與關如何判斷、設定項目不出現的原因、與影格生成的關係,以及安全的比較步驟。
WPR/WPA 實務 ── 從系統整體調查「整台 PC 變慢」
工作管理員追不到的「整台 PC 變慢」「開機很慢」,可用 WPR/WPA 擷取整個 OS 的 ETW 追蹤再讀。本文說明 wpr.exe 擷取步驟,以及在 WPA 裡怎麼讀 CPU、等待與磁碟 I/O。
WPF 高 DPI 對應──「不是應該對 DPI 很強嗎」卻仍模糊、暈邊的原因與對策
WPF 以 DIP(1/96 英吋)進行版面配置,一開始就是 System DPI Aware,但移到 DPI 不同的螢幕時整體會暈邊,點陣圖與細線也會模糊。本文從實務角度整理原因判斷、.NET Framework 4.6.2/.NET 上的 Per-Monitor DPI...
WinForms 的高DPI支援 - 4K螢幕顯示模糊、版面跑掉的原因與實務對策
本文從 DPI 虛擬化與 DPI 感知模式(System Aware / Per-Monitor V2)的角度,整理 WinForms 應用程式在4K螢幕、150%縮放環境下顯示模糊、版面跑掉的原因,並說明 .NET 與 .NET Framework 各自的設定方式、Aut...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
UI 執行緒 & 計時器
整理 WPF / WinForms UI 執行緒、非同步流程、Dispatcher 使用、計時器判斷的主題頁面。
常見問題
整理諮詢這個主題時常見的問題。
- 一直顯示剩餘1秒,是發生故障了嗎?
- 光看顯示無法判斷。需要區分速度預測失準、最後的處理階段、畫面沒有更新,以及工作確實停滯。檢查處理數量與記錄是否變化,以及是否有等待確認的視窗,不要只因剩餘1秒這個數字就強制結束應用程式。
- 進度達到99%,是否代表只剩總時間的1%?
- 不是。99%的意義取決於它衡量的是數量、資料量還是階段權重,而且每個單位的處理時間不一定相同。進度百分比是工作量的比例,不是剩餘時間本身。
- 圓形動畫還在轉,就能證明工作正常嗎?
- 不能。如果畫面動畫與實際處理獨立運作,工作處於等待狀態時,動畫也能繼續轉動。應把動畫與處理數量、階段變化、記錄等實際進展的證據分開看。
- 無法準確估算剩餘時間,就不能顯示進度列嗎?
- 可以顯示。只要知道總量與已完成量,就能顯示比例;速度不穩定時可以只省略剩餘時間。如果連總量也未知,應顯示階段或已處理數量,而不是編造百分比。