原因:
某些耗時(shí)操作阻塞了主線程。
理解上述原因,需先搞清楚Winform線程機(jī)制。主要有以下2點(diǎn)特性:1.單線程模型;2.依賴消息循環(huán)。
1.單線程模型
Winform 默認(rèn)是單線程。通常,所有的UI操作,包括控件更新、事件處理,都由主線程管理(也就是UI線程)。
任何在事件處理程序中運(yùn)行的代碼都會(huì)占用主線程。如果某個(gè)事件中有耗時(shí)很多的操作,就會(huì)阻塞線程。比如有時(shí)PictureBox.Image的顯示會(huì)滯后。
下面代碼中,pictureBox1的顯示會(huì)有滯后。背后的工作原理是:
“pictureBox1.Image = image0”運(yùn)行完成后,系統(tǒng)只是將 pictureBox1 的 Image 屬性更新,但不會(huì)立即觸發(fā)控件繪制。(實(shí)際的繪制操作是由消息循環(huán)處理的,在 WM_PAINT 消息中完成)
pictureBox1的屬性設(shè)置之后,如果緊接著有耗時(shí)操作占用UI線程,就會(huì)導(dǎo)致WM_PAINT無(wú)法及時(shí)處理,所以不能及時(shí)繪制。
最佳解決辦法:
避免在 UI 線程執(zhí)行耗時(shí)任務(wù)。將耗時(shí)任務(wù)移到后臺(tái)線程,可以確保 UI 線程始終空閑以處理消息循環(huán),比如使用 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);
......
}
}
補(bǔ)充一點(diǎn):只有主線程可以訪問(wèn)或更新控件;如果要從其他線程訪問(wèn)、更新控件,要使用特定的線程間通訊,必須使用 Control.Invoke 或 Control.BeginInvoke 方法將操作封送到 UI 線程執(zhí)行。如果在后臺(tái)線程中得到某個(gè)變量,需要渲染到主界面,就需要用到Control.Invoke等方法。
2.依賴消息循環(huán)
UI線程依賴消息循環(huán)(Message Loop)處理用戶輸入、標(biāo)點(diǎn)擊等操作和系統(tǒng)消息。Application.Run 啟動(dòng)時(shí),UI線程進(jìn)入消息循環(huán)狀態(tài),不斷從消息隊(duì)列中提取消息并分發(fā)到對(duì)應(yīng)控件處理。上述提到的線程阻塞實(shí)際上是阻塞了消息循環(huán)。
上述2個(gè)特性明白之后,就能理解原因中提到的阻塞了。如果不理解,可以嘗試在pictureBox1.Image之后跟隨一個(gè)簡(jiǎn)單的耗時(shí)操作幫助理解。
轉(zhuǎn)自https://www.cnblogs.com/snowoct/p/18690950
該文章在 2025/2/7 9:44:38 編輯過(guò)