简体   繁体   English

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

[英]Why does Thread.Sleep() freeze the Form?

I try to experiment with Thread.Sleep() .我尝试尝试Thread.Sleep() I created basic Windows Forms application with one button.我用一个按钮创建了基本的 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         
    }

When I click my button the DoStuff method works fine, but the GUI freezes and nothing happens.当我单击按钮时, DoStuff方法工作正常,但 GUI 冻结并且没有任何反应。 Can someone explain me why?有人可以解释我为什么吗?

Thread.Sleep just sleeps the current thread (ie stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread. Thread.Sleep只是让当前线程休眠(即阻止它执行任何操作,例如重绘、处理点击等),在您的情况下是 UI 线程。 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.如果您将Sleep放在DoStuff您将不会像在单独的线程上一样遇到块,尽管您将无法更新 button1。 Depending on the version of .NET you're using consider using the Task Parallel Library, something like this:根据您使用的 .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);
            }
        });
}

To keep the UI active, you need for the main UI thread to service its message pump.要保持 UI 处于活动状态,您需要主 UI 线程为其消息泵提供服务。 It can only do that when it is not handling UI events.它只能在不处理 UI 事件时执行此操作。 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.在大约100000*500毫秒内不会返回。 While this event handler is executing, the UI thread is busy.当此事件处理程序正在执行时,UI 线程正忙。 It is executing this event handler.它正在执行此事件处理程序。 As such it is not able to service the message pump.因此,它无法为消息泵提供服务。 Hence your application's UI freezes.因此您的应用程序的 UI 冻结。

For that you better use a Timer but if you want your current code to work you need to add Application.DoEvents();为此,您最好使用Timer但如果您希望当前的代码正常工作,则需要添加Application.DoEvents(); after you update the button.Label += "."更新button.Label += "."

If you're new to multithreading, I strongly encourage you to look at the Task Parallel Library (TPL).如果您不熟悉多线程,我强烈建议您查看任务并行库 (TPL)。 It simplifies threading, and gives you tools to help guarantee callback (continuation) threads occur on the UI thread.它简化了线程,并为您提供了帮助保证回调(继续)线程在 UI 线程上发生的工具。

The TPL is in the System.Threading.Tasks namespace. TPL 位于 System.Threading.Tasks 命名空间中。

Update: just seen your comment about .Net v2.更新:刚刚看到您对 .Net v2 的评论。 TPL was introduced in .NET v3.5 or possibly as late as v4. TPL 是在 .NET v3.5 中引入的,也可能晚于 v4。

EDIT: After programming for a few more years, I now know how terrible of a practice this is. EDIT:在编程几年之后,我现在知道这是一种多么糟糕的做法。 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. Sleep()只是将当前线程暂停int毫秒,如果您的整个程序运行 1 个线程,它将暂停整个程序。 Don't quote me on this, I do believe that async creates a new thread specifically for that function.不要引用我的话,我相信 async 专门为该函数创建了一个新线程。

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.要调用函数 sleep asleep(milliseconds) ,请将“毫秒”替换为您希望睡眠的毫秒数。

Function Code:功能代码:

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

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)现在您在单独的线程中运行您的 WORK 并释放您的 UI 线程以进行日常工作(绘图相关或其他工作)

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#?更多关于调用 - 如何从 C# 中的另一个线程更新 GUI?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM