Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么Thread.Sleep()会冻结表单?_C#_Multithreading_User Interface_Sleep_Freeze - Fatal编程技术网

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

C# 为什么Thread.Sleep()会冻结表单?,c#,multithreading,user-interface,sleep,freeze,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()
进行实验。我用一个按钮创建了基本的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         
    }
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
对于(int i=0;i<100000;i++)
{
睡眠(500);
按钮1.文本+=”;
}
}
公共空间
{
//多斯塔夫
}

当我点击我的按钮时,
DoStuff
方法工作正常,但是GUI冻结,什么也没有发生。有人能解释一下原因吗?

线程。睡眠只会睡眠当前线程(即阻止它做任何事情,如重画、处理单击等),在您的情况下,这就是UI线程。如果将
Sleep
放入
DoStuff
中,您将不会体验到阻塞,因为您将处于单独的线程中,尽管您无法更新按钮1。根据.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);
            }
        });
}
private TaskScheduler\u uiScheduler;
公共表格1()
{
初始化组件();
_uiScheduler=TaskScheduler.FromCurrentSynchronizationContext();
}
私有无效按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
//在新线程上创建任务。
Task.Factory.StartNew(()=>
{
对于(int i=0;i<100000;i++)
{
睡眠(500);
//在UI线程上创建新任务以更新按钮
Task.Factory.StartNew(()=>
{button1.Text+=“;”},CancellationToken.None,TaskCreationOptions.None,_uiScheduler);
}
});
}

重新排列代码,如下所示

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
        }
}
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
}
公共空间
{     
对于(int i=0;i<100000;i++)
{
睡眠(500);
//调用在这里
}
}
现在,您可以在一个单独的线程中运行您的工作,并为日常工作(与绘图相关或其他工作)释放UI线程

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


有关调用的更多信息-

要保持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 +=".";
    }
}
private void按钮1\u单击(对象发送者,事件参数e)
{
螺纹1=新螺纹(DoStuff);
thread1.Start();
对于(int i=0;i<100000;i++)
{
睡眠(500);
按钮1.文本+=”;
}
}

大约在
100000*500
毫秒内不返回。此事件处理程序执行时,UI线程正忙。它正在执行此事件处理程序。因此,它无法维修信息泵。因此,应用程序的UI会冻结。

为此,您最好使用
计时器,但如果您希望当前代码正常工作,则需要添加
application.DoEvents()
更新
按钮后,如果您是多线程新手,我强烈建议您查看任务并行库(TPL)。它简化了线程,并为您提供了一些工具来帮助确保回调(延续)线程在UI线程上发生

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


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

编辑:
经过几年的编程,我现在知道这是一种多么可怕的做法。不要做我下面建议的任何事情。都是废话。一个更合适的解决方案是将所有密集型方法都异步地放在一起。无论如何,不要做我下面提到的事情

以上所有方法都可以,但我建议只使用异步void。
Sleep()。不要引用我的话,我相信async会专门为该函数创建一个新线程

下面我介绍了一个更好的睡眠功能

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

功能代码:

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

我知道UI会休眠,但我不明白的是为什么它会休眠超过500毫秒?@GugaMelkadze只有当
按钮1\u Click()
方法返回时,UI才能更新,而且很明显,它在休眠很长时间后才会返回。谢谢,但我使用的是.NET 2.0。你的Thread.sleep()调用是for{}的一部分语句,因此它被执行100000次。您当前的UI线程已休眠近14个小时@GugaMelkadze如果你使用的是非常过时的.Net版本,你应该在你的问题中添加一个标签,否则人们会认为你没有使用过时的版本,并且会浪费他们的时间用一个不可用的解决方案回答您的问题。您需要调用
文本
访问,因为它位于与创建控件的线程不同的线程上。我在编辑中添加了该部分:)只是出于兴趣,您使用的是.Net的哪个版本?@MatthewWatson我使用的是.Net 2.0永远不要使用
Application.DoEvents()
!这是邪恶的。是的,我不会。我会使用每500毫秒一次的计时器