代码为什么在线程休眠前不执行?

3

I do this:

clear();
coinRefundComplete.Visible = true;
state = 0;

System.Threading.Thread.Sleep(4000);
clear();

greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;

我希望"coinRefundComplete"文字(它是一个标签)会出现4秒钟,然后被我用clear()方法清除,然后发生一些其他的事情。但是,当我用第一个clear()清除表单后,我的表单会在4秒钟内变成空白,然后正常结束。


2
你正在阻塞UI线程。更改将不可见,你永远不应该阻塞它。如果你想要显示某些东西一会儿,请使用计时器。 - Sami Kuhmonen
1
如果所有这些代码都在一个方法中 - 那么窗体直到方法完全执行后才会更新。 - Fabio
1
System.Threading.Thread.Sleep(4000); 之前插入 Application.DoEvents(); - 给 UI 一个机会来 重绘 标签。 - Dmitry Bychenko
@Fabio,你的意思是我不能在一个方法中多次清除表单吗? - 73est
1
@DmitryBychenko - Application.DoEvents()很糟糕。和在UI线程上调用Thread.Sleep(...)一样糟糕,甚至更糟。 - Enigmativity
3个回答

11

使用 async/await 方法。
将您的方法设为async - 下面是按钮点击事件处理程序的示例:

private async void ButtonClick(object sender, EventArgs e)
{
    clear();
    coinRefundComplete.Visible = true;
    state = 0;

    await Task.Delay(4000);
    clear();

    greeting.Visible = true;
    rate.Visible = true;
    refundTicket.Visible = true;
    currentTime.Visible = true;
}

当执行await Task.Delay(4000);时,UI线程将被释放,这将更新之前所做的所有更改。 4秒后,方法将继续在UI线程上执行。


谢谢,这正是我在寻找的。我使用了这种技术找到了多个问题和答案,但没有一个指出我需要在按钮单击的方法定义中添加“async”。 - 73est
尽管我认为这个实现更好,因为它不会冻结用户界面,但我将另一个答案标记为正确的,因为它提供了一个使用与我的原始问题相同的思路的解决方案(虽然不是一个很好的整体解决方案)。 - 73est
1
@73est - 另一种解决方案是一个糟糕的想法。它不能保证有效。你永远不应该只是让UI线程睡眠。这个答案更好。 - Enigmativity
我理解了,并且在我的实现中使用了这个解决方案。但是就目前而言,另一个解决方案回答了我的问题,即在这种情况下如何理论上使用Thread.Sleep()。 - 73est

0

虽然让GUI线程休眠并不可取,但在进入睡眠状态之前允许GUI线程刷新控件状态将显示您想要的更改。在使其可见并进入睡眠状态之前调用Control.UpdateControl.Refresh,以便GUI线程能够在进入睡眠状态之前显示更改。

clear();
coinRefundComplete.Visible = true;
label1.Update();
state = 0;    
System.Threading.Thread.Sleep(4000);
clear();

在使用Thread.Sleep时要小心,因为在您的情况下它是GUI线程,而GUI将在您睡眠的时间内无响应。了解您想要阻止线程的原因可能会带来其他更好的建议。

编辑

您可以使用其他线程添加延迟,而不会阻塞GUI线程。如果您可以使用框架4.5,则可以使用async / await结构或阅读本文在没有.NET Framework 4.5的情况下使用async/await

private async void testAsyncAwaitDely_Click(object sender, EventArgs e)
{
     clear();
     coinRefundComplete.Visible = true;
     state = 0;
     await Task.Delay(4000);
     clear();
     //Your code
}

-3

更新 [22-Apr-2018]: 我没有删除这个答案,以便您不会走错路,尽管我的答案确实是 OP 问题的 一个 可能解决方案(特别是当它花费了我一些负面声誉)。您应该阅读下面的帖子,真正说服自己为什么 Application.DoEvents 不是解决此问题的好方法:


你的UI没有刷新是因为你在UI线程上进行了整个处理,所以UI线程没有机会刷新UI元素。你需要在任何你希望UI被刷新并加载最新更改的地方调用Application.DoEvents()函数。这是你修改后的代码。我在当前UI线程调用sleep之前添加了一行代码:

        clear();
        coinRefundComplete.Visible = true;
        state = 0;

        //new line of code
        System.Windows.Forms.Application.DoEvents();

        System.Threading.Thread.Sleep(4000);
        clear();

        greeting.Visible = true;
        rate.Visible = true;
        refundTicket.Visible = true;
        currentTime.Visible = true;

1
调用Application.DoEvents()是非常糟糕的。非常糟糕。它可能导致各种重入错误。它只是为了与VB6的向后兼容而提供的。无论如何都要避免使用它。 - Enigmativity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接