为什么Thread.Sleep()会冻结表单?

时间:2014-12-08 10:43:22

标签: c# multithreading user-interface sleep freeze

我尝试使用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冻结并且没有任何反应。有人能解释我为什么吗?

6 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:3)

要使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冻结。

答案 2 :(得分:0)

为此,您最好使用Timer,但如果您希望当前的代码有效,则需要在更新Application.DoEvents();后添加button.Label += "."

答案 3 :(得分:0)

如果您是多线程新手,我强烈建议您查看任务并行库(TPL)。它简化了线程,并为您提供工具来帮助保证UI线程上发生回调(延续)线程。

TPL位于System.Threading.Tasks命名空间中。

更新:刚看到你对.Net v2的评论。 TPL是在.NET v3.5中引入的,也可能是在v4中引入的。

答案 4 :(得分:0)

所有上述方法都有效,但我建议只使用async void Sleep()只暂停当前线程int毫秒,如果整个程序运行1个线程,它将暂停整个程序。不要引用我这个,我相信async会专门为该函数创建一个新线程。

下面我提到了一个更好的睡眠功能。

要调用函数asleep(milliseconds), 将“毫秒”替换为您希望睡眠的毫秒数。

功能代码:

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

答案 5 :(得分:-1)

重新安排代码如下

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线程以进行常规工作(绘图相关或其他工作)

注意 - 现在您需要调用方法来更改按钮文本,否则您将收到警告&#34;跨线程操作无效&#34;

有关Invokes的更多信息 - How to update the GUI from another thread in C#?