2

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?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
GugaMelkadze
  • 157
  • 1
  • 3
  • 11

6 Answers6

6

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);
            }
        });
}
DoctorMick
  • 6,703
  • 28
  • 26
  • I understand that UI sleeps , what I don't understand is why does it sleep more then 500 milliseconds ? – GugaMelkadze Dec 08 '14 at 10:48
  • @GugaMelkadze The UI can only be updated when the `button1_Click()` method returns, and clearly it won't return until it has slept for a very long time. – Matthew Watson Dec 08 '14 at 10:49
  • Thanks but I'm using .NET 2.0. – GugaMelkadze Dec 08 '14 at 10:50
  • Your Thread.Sleep() call is part of your for{} statement, so it gets executed 100,000 times. You currently have your UI thread sleeping for almost 14 hours! – olitee Dec 08 '14 at 10:51
  • @GugaMelkadze If you are using a very outdated version of .Net, you should add a tag to your question, otherwise people will assume you are not using an obsolete version, and will waste their time answering your question with an unusable solution. – Matthew Watson Dec 08 '14 at 10:52
  • @MatthewWatson Thanks, I'll consider that. – GugaMelkadze Dec 08 '14 at 11:11
4

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
0

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

Darien Pardinas
  • 5,910
  • 1
  • 41
  • 48
0

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.

olitee
  • 1,683
  • 10
  • 12
0

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

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#?

Community
  • 1
  • 1
Kavindu Dodanduwa
  • 12,193
  • 3
  • 33
  • 46