Why does Thread.Sleep() freeze the Form?

こ雲淡風輕ζ 提交于 2019-12-02 03:49:24

问题


I try to experiment with Thread.Sleep(). I created basic Windows Forms application with one button.

    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         
    }

When I click my button the DoStuff method works fine, but the GUI freezes and nothing happens. Can someone explain me why?


回答1:


To keep the UI active, you need for the main UI thread to service its message pump. It can only do that when it is not handling UI events. In your case the function

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 +=".";
    }
}

does not return for around 100000*500 milliseconds. While this event handler is executing, the UI thread is busy. It is executing this event handler. As such it is not able to service the message pump. Hence your application's UI freezes.




回答2:


Thread.Sleep just sleeps the current thread (i.e. stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread. If you put the Sleep in DoStuff you wouldn't experience the block as you'd be on a separate thread although you wouldn't be able to update button1. Depending on the version of .NET you're using consider using the Task Parallel Library, something like this:

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);
            }
        });
}



回答3:


For that you better use a Timer but if you want your current code to work you need to add Application.DoEvents(); after you update the button.Label += "."




回答4:


If you're new to multithreading, I strongly encourage you to look at the Task Parallel Library (TPL). It simplifies threading, and gives you tools to help guarantee callback (continuation) threads occur on the UI thread.

The TPL is in the System.Threading.Tasks namespace.

Update: just seen your comment about .Net v2. TPL was introduced in .NET v3.5 or possibly as late as v4.




回答5:


EDIT: After programming for a few more years, I now know how terrible of a practice this is. DO NOT DO ANYTHING I SUGGESTED BELOW. It's all crap. A more proper solution would be doing all of your intensive methods async all together. Regardless, don't do what I mention below.

All The methods above do work however, I do recommend just using an async void.
Sleep() just pauses the current thread for int amount of milliseconds, and if your whole program runs off of 1 thread, it'll pause the whole program. Don't quote me on this, I do believe that async creates a new thread specifically for that function.

Below I've included a better sleep function.

To call the function asleep(milliseconds), replace "milliseconds" with how many milliseconds you wish to sleep for.

Function Code:

public async void asleep(int time){
    await Task.Delay(time)
}



回答6:


Re-arrange code as following

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
        }
}

Now you run your WORK in a separate thread and release your UI thread for usual work (Drawing related or other work)

NOTE - Now you will need Invoke methods to change Button text , else you will get warning for "Cross-thread operation not valid"

More on Invokes - How to update the GUI from another thread in C#?



来源:https://stackoverflow.com/questions/27356165/why-does-thread-sleep-freeze-the-form

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!