簡體   English   中英

為什么 Thread.Sleep() 凍結表單?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM