[英]Why does Thread.Sleep() freeze the Form?
我嘗試嘗試Thread.Sleep()
。 我用一個按鈕創建了基本的 Windows 窗體應用程序。
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
public void DoStuff()
{
//DoStuff
}
當我單擊按鈕時, DoStuff
方法工作正常,但 GUI 凍結並且沒有任何反應。 有人可以解釋我為什么嗎?
Thread.Sleep
只是讓當前線程休眠(即阻止它執行任何操作,例如重繪、處理點擊等),在您的情況下是 UI 線程。 如果您將Sleep
放在DoStuff
您將不會像在單獨的線程上一樣遇到塊,盡管您將無法更新 button1。 根據您使用的 .NET 版本,請考慮使用任務並行庫,如下所示:
private TaskScheduler _uiScheduler;
public Form1()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
// Create a task on a new thread.
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
// Create a new task on the UI thread to update the button
Task.Factory.StartNew(() =>
{ button1.Text += "."; }, CancellationToken.None, TaskCreationOptions.None, _uiScheduler);
}
});
}
要保持 UI 處於活動狀態,您需要主 UI 線程為其消息泵提供服務。 它只能在不處理 UI 事件時執行此操作。 在你的情況下,功能
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
button1.Text +=".";
}
}
在大約100000*500
毫秒內不會返回。 當此事件處理程序正在執行時,UI 線程正忙。 它正在執行此事件處理程序。 因此,它無法為消息泵提供服務。 因此您的應用程序的 UI 凍結。
為此,您最好使用Timer
但如果您希望當前的代碼正常工作,則需要添加Application.DoEvents();
更新button.Label += "."
如果您不熟悉多線程,我強烈建議您查看任務並行庫 (TPL)。 它簡化了線程,並為您提供了幫助保證回調(繼續)線程在 UI 線程上發生的工具。
TPL 位於 System.Threading.Tasks 命名空間中。
更新:剛剛看到您對 .Net v2 的評論。 TPL 是在 .NET v3.5 中引入的,也可能晚於 v4。
EDIT:
在編程幾年之后,我現在知道這是一種多么糟糕的做法。 不要做我在下面建議的任何事情。 都是廢話。 更合適的解決方案是將所有密集型方法全部異步執行。 無論如何,不要做我下面提到的事情。
但是,上述所有方法都有效,我建議僅使用異步無效。
Sleep()
只是將當前線程暫停int
毫秒,如果您的整個程序運行 1 個線程,它將暫停整個程序。 不要引用我的話,我相信 async 專門為該函數創建了一個新線程。
下面我包含了一個更好的睡眠功能。
要調用函數 sleep asleep(milliseconds)
,請將“毫秒”替換為您希望睡眠的毫秒數。
功能代碼:
public async void asleep(int time){
await Task.Delay(time)
}
重新排列代碼如下
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(DoStuff);
thread1.Start();
}
public void DoStuff()
{
for (int i = 0; i < 100000; i++)
{
Thread.Sleep(500);
//Invoke goes here
}
}
現在您在單獨的線程中運行您的 WORK 並釋放您的 UI 線程以進行日常工作(繪圖相關或其他工作)
注意 - 現在您將需要調用方法來更改按鈕文本,否則您將收到“跨線程操作無效”的警告
更多關於調用 - 如何從 C# 中的另一個線程更新 GUI?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.