原因:
某些耗時操作阻塞了主線程。
理解上述原因,需先搞清楚Winform線程機制。主要有以下2點特性:1.單線程模型;2.依賴消息循環。
1.單線程模型
Winform 默認是單線程。通常,所有的UI操作,包括控件更新、事件處理,都由主線程管理(也就是UI線程)。
任何在事件處理程序中運行的代碼都會占用主線程。如果某個事件中有耗時很多的操作,就會阻塞線程。比如有時PictureBox.Image的顯示會滯后。
下面代碼中,pictureBox1的顯示會有滯后。背后的工作原理是:
“pictureBox1.Image = image0”運行完成后,系統只是將 pictureBox1 的 Image 屬性更新,但不會立即觸發控件繪制。(實際的繪制操作是由消息循環處理的,在 WM_PAINT 消息中完成)
pictureBox1的屬性設置之后,如果緊接著有耗時操作占用UI線程,就會導致WM_PAINT無法及時處理,所以不能及時繪制。
最佳解決辦法:
避免在 UI 線程執行耗時任務。將耗時任務移到后臺線程,可以確保 UI 線程始終空閑以處理消息循環,比如使用 Task.Run 或 async/await。
private void button2_Click(object sender, EventArgs e)
{
using (var session = new InferenceSession(_modelPath))
{
......
Bitmap image0 = new Bitmap(_imagePath);
pictureBox1.Image = image0;
DenseTensor<float> inputTensor = PreprocessImage(image0);
......
}
}
補充一點:只有主線程可以訪問或更新控件;如果要從其他線程訪問、更新控件,要使用特定的線程間通訊,必須使用 Control.Invoke 或 Control.BeginInvoke 方法將操作封送到 UI 線程執行。如果在后臺線程中得到某個變量,需要渲染到主界面,就需要用到Control.Invoke等方法。
2.依賴消息循環
UI線程依賴消息循環(Message Loop)處理用戶輸入、標點擊等操作和系統消息。Application.Run 啟動時,UI線程進入消息循環狀態,不斷從消息隊列中提取消息并分發到對應控件處理。上述提到的線程阻塞實際上是阻塞了消息循環。
上述2個特性明白之后,就能理解原因中提到的阻塞了。如果不理解,可以嘗試在pictureBox1.Image之后跟隨一個簡單的耗時操作幫助理解。
轉自https://www.cnblogs.com/snowoct/p/18690950
該文章在 2025/2/7 9:44:38 編輯過