在c#winforms应用程序中,为什么';这种睡眠循环不起作用吗?

在c#winforms应用程序中,为什么';这种睡眠循环不起作用吗?,c#,visual-studio,winforms,C#,Visual Studio,Winforms,这不是重复的,因为公认的答案是他混淆了毫秒和秒 注意-有人建议这样可以解决我的问题。这个问题中的例子非常复杂,因为它有很多不相关的东西,答案迎合了这些,包括了所有的复杂性。理解这个问题和答案要困难得多。我的问题要简单得多,因此答案要简单得多。此处接受的答案没有标记。ThrowIfCancellationRequested()很难将该链接的复杂答案与我的简单问题相适应。我当然不会接受对这个问题的公认答案作为对我的答案。汉斯实际上给出了一个简单的答案。我不认为一句话的答案适用于那个问题。所以我的问题

这不是重复的,因为公认的答案是他混淆了毫秒和秒

注意-有人建议这样可以解决我的问题。这个问题中的例子非常复杂,因为它有很多不相关的东西,答案迎合了这些,包括了所有的复杂性。理解这个问题和答案要困难得多。我的问题要简单得多,因此答案要简单得多。此处接受的答案没有
标记。ThrowIfCancellationRequested()很难将该链接的复杂答案与我的简单问题相适应。我当然不会接受对这个问题的公认答案作为对我的答案。汉斯实际上给出了一个简单的答案。我不认为一句话的答案适用于那个问题。所以我的问题和那个不同


我有一个带按钮和标签的表单

单击按钮时,我有此代码

单击按钮将调用执行此操作的函数

            int a, b;           
            int i=0;
            while(i<5)
            {
                i += 1;
                Thread.Sleep(1000);
                label1.Text += "a";               
            }
            MessageBox.Show("done");
inta,b;
int i=0;

while(i您的方法必须将控制权返回给调用者,以便消息泵处理将呈现新文本的重绘事件

老路

void SomeHandler(object sender, EventArgs e)
{
    int a, b;           
    int i=0;
    while(i<5)
    {
        i += 1;
        Thread.Sleep(1000);
        Application.DoEvents(); //Process events in the event queue
        label1.Text += "a";               
    }
    MessageBox.Show("done");
}
void SomeHandler(对象发送方,事件参数e)
{
INTA,b;
int i=0;

而在我看来,异步方法是最干净的

不过,这里还有另一种方法,演示了如果您采用更经典的任务/线程样式,如何使用Invoke()更新UI:

    private void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;

        Task.Run(() => {
            int i = 0;
            while (i < 5)
            {
                i += 1;
                System.Threading.Thread.Sleep(1000);
                label1.Invoke((MethodInvoker) delegate {
                    label1.Text += "a";
                });

            }
            button1.Invoke((MethodInvoker) delegate {
                MessageBox.Show("done");
                button1.Enabled = true;
            });             
        });
    }
private void按钮1\u单击(对象发送者,事件参数e)
{
按钮1.启用=错误;
Task.Run(()=>{
int i=0;
而(i<5)
{
i+=1;
系统线程线程睡眠(1000);
label1.Invoke((MethodInvoker)委托{
标签1.Text+=“a”;
});
}
button1.Invoke((MethodInvoker)委托{
MessageBox.Show(“完成”);
按钮1.启用=真;
});             
});
}

只是一个建议-使用Microsoft的反应式框架(Rx)-NuGet
System.Reactive.Windows.Forms
并使用System.Reative.Linq;
在代码顶部弹出一个
。然后您可以编写:

Observable
    .Interval(TimeSpan.FromSeconds(1.0))
    .Take(5)
    .ObserveOn(this)
    .Subscribe(_ => label1.Text += "a");

或者另一种方法-使用计时器。我喜欢代码的简单性。我只是测试了“旧方法”它同时打印最后两个a。每个a和每个a之间都应该有一个停顿。我认为在最后一个a和完成之间没有停顿。因此,该代码的唯一问题是它同时打印最后两个a。为什么它同时打印最后两个a?你的新方法没有这个错误。@noseratio啊,好的,所以重新进入不一定是一个问题,除非重新进入是非计划的/冲突的/未经测试的。很高兴知道。谢谢请删除关于
Application.DoEvents()
-这只是VB6中的旧方法。在.NET中从来不是这种方法。@barlop-重新进入是
DoEvents()的一个严重问题
-它会以一个bug的形式出现在看似不相关的代码中,在你意识到它是
DoEvents()
之前,你会浪费一整天的调试时间。它不是缓冲,当你有意编写“不做任何事”代码时,Label.OnPaint()方法无法运行。添加
label1.Update();
在这段代码中,学习如何使用计时器或工作线程可以等到以后。@HansPassant谢谢,这太神奇了。在
label1.Update();
后面加上一行
label1.text+=“a”;
。你可以把它作为一个答案。什么算是“不做任何其他事情”代码"?同一线程中的顺序代码?@barlop那么你可以在5秒钟内阻止UI吗?@nosratio我只是在代码运行时禁用按钮,这样可以防止用户单击按钮,从而阻止UI的这一方面,这是UI执行代码的唯一方面计数刻度
if(tmrblahtickcount==5){timer1.enabled=false;tmrblahtickcount=0;thingtoruntertimer();}
像这样对
任务进行一次“开火并忘记”调用。运行
很少是个好主意。有一个
I进程
-模式用于此,但我怀疑OP需要一个背景线程。谢谢,这真的很有趣,顺便说一句,我看到messagebox.show不一定在其中。调用body这个答案也向我显示了这一点。我nvoke body,button1.enabled行导致在另一个线程中调用它时出现异常。这很有趣,因为它让我看到在异步方法的情况下,它是同一个线程,因此没有异常,我从打印System.Threading.thread.CurrentThread.ManagedThreadId中看到了这一点,而这里它是另一个线程。I还可以看到,可以这样做。invoke或button1.invoke(对于任何控件),“因为它”在拥有控件的底层窗口句柄的线程上执行指定的委托。如前所述,MessageBox位于invoke主体中,因此直到对话框关闭后按钮才会启用。
Observable
    .Interval(TimeSpan.FromSeconds(1.0))
    .Take(5)
    .ObserveOn(this)
    .Subscribe(_ => label1.Text += "a");