C# 如何使用计时器等待?
我试图通过使用计时器来延迟方法中的事件,但是我不一定理解如何使用计时器来等待 我将计时器设置为2秒,但当我运行此代码时,最后一次调用不会延迟2秒C# 如何使用计时器等待?,c#,winforms,timer,sleep,C#,Winforms,Timer,Sleep,我试图通过使用计时器来延迟方法中的事件,但是我不一定理解如何使用计时器来等待 我将计时器设置为2秒,但当我运行此代码时,最后一次调用不会延迟2秒 Timer timer = new Timer(); timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called timer.Interval = (1000) * (2); // Timer w
Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000) * (2); // Timer will tick evert second
timer.Enabled = true; // Enable the timer
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
label1.Text = "second";
}
因此,当我点击我的按钮时,它会立即将label1显示为“秒”,而不是更改为“第一”,等待2秒,然后更改为“秒”。我在这里读了很多关于使用计时器而不是thread.sleep的文章,但我似乎无法找到/弄清楚如何真正实现它。如果你想做的只是在计时器滴答作响时更改文本,你会不会更好地推迟
label1.Text = "second";
…在将计时器更改为enabled=false之前或之后的计时器滴答声中
就这样,
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}
timer.Start()
仅启动计时器,但当计时器在后台运行时立即返回。因此,在将标签文本设置为first
和second
之间几乎没有停顿。您要做的是等待计时器计时,然后再次更新标签:
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}
顺便说一句,您不应该将timer.Enabled
设置为true,您已经在使用timer.Start()
启动计时器
如注释中所述,您可以将计时器创建放入一个方法中,如下所示(注意:这是未测试的):
然后你可以这样使用它:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
Delayed(2000, () => label1.Text = "second");
}
特吉弗的后续行动
使用延迟是否包含内存泄漏(引用泄漏)
订阅事件始终会创建双向引用
在本例中,timer.Tick
获取对匿名函数(lambda)的引用。该函数提升一个局部变量timer
,尽管它是一个引用,而不是一个值,并且包含对传入操作委托的引用。该委托将包含对标签1
的引用,该标签是表单的一个实例成员。那么从计时器到表单是否有循环引用
我不知道答案,我觉得有点难以推理。因为我不知道,我会在Delayed
中删除lambda的使用,使其成为一个合适的方法,并且让它除了停止计时器(该方法的sender
参数)之外,还删除事件
通常lambda不会导致垃圾收集问题。在这种情况下,计时器实例仅在本地存在,lambda中的引用不会阻止垃圾收集来收集实例(另请参阅)
实际上,我使用.NET内存分析器再次测试了这一点。计时器对象收集得很好,没有发生泄漏。探查器确实给了我一个警告,有一些实例“[…]被垃圾收集而没有被正确处理”。但是,移除事件处理程序本身(通过保留对它的引用)并不能解决这一问题。将捕获的计时器引用更改为(timer)s
,也不会改变这一点
显然,停止计时器后在事件处理程序中调用timer.Dispose()
,这有帮助,但我认为这是否确实必要。我认为探查器警告/注释没有那么重要。如果您使用的是C#5.0wait
,那么这就容易多了:
使用Sysmtes.Timers.Timer
此代码不起作用,您需要封送到UI线程。窗体
计时器的优点是您不需要这样做。这是真的,忘了它吧。我要么删除该部分,要么在其中添加一些invokey内容,只是不想混淆OP。因此,如果我有很多地方需要等待,那么每次等待都会有一个唯一的计时器,对吗?是的,您可以将样板计时器创建抽象为一个函数,然后像delay(2000,()=>label1.Text=“second”)那样调用它;代码>。我遇到的问题是,否则该方法将调用大约6个事件,每个事件都需要等待,这会导致问题,因为timer.start()在继续之前不会等待计时器执行,它只是启动计时器,然后继续下一行。啊,答案是,当您启动System.Windows.Forms.timer时,它创建(并保存)System.Windows.Forms.NativeWindow对象,该对象将自身添加到将本机窗口句柄与NativeWindow对象关联的查找表中。当计时器被销毁时,该对象将从地图中删除。因此,有一个引用在它工作时保持它的活动状态。为了准确起见,Timer创建了一个Timer.TimerNativeWindow的子类Timer.TimerNativeWindow对象,它有一个指向Timer对象的后指针,因此是保持活动状态的引用。好主意;我认为这是最干净的方法。然而,我认为“async”关键字必须在“void”关键字之前。这个想法太糟糕了。WinForms事件中的延迟会导致奇怪的行为(通过暂停消息泵)。WinForms是单身-threaded@smirkingman您只需自己运行代码就可以看到,因为这是异步的,所以消息泵没有被阻塞。Winforms也不是“单线程”。您应该只从一个线程与UI进行交互,但您完全可以使用其他线程进行非UI工作,而不是因为此特定问题需要(或使用)任何其他线程来解决此问题而不阻塞UI。
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
Delayed(2000, () => label1.Text = "second");
}
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
await Task.Delay(2000);
label1.Text = "second";
}
private bool Delay(int millisecond)
{
Stopwatch sw = new Stopwatch();
sw.Start();
bool flag = false;
while (!flag)
{
if (sw.ElapsedMilliseconds > millisecond)
{
flag = true;
}
}
sw.Stop();
return true;
}
bool del = Delay(1000);