在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)-NuGetSystem.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");