Thread.Sleep
,System.Windows.Forms.Timer
和Monitor.Wait
。但我似乎无法弄清楚如何等待X秒然后执行其他操作 - 而不锁定线程。我有一个带有按钮的表单。单击该按钮后,计时器将启动并等待5秒钟。在这5秒钟后,表单上的其他控件将变为绿色。使用
Thread.Sleep
会导致整个应用程序在5秒钟内无响应 - 那么我该如何“等待5秒钟后执行某些操作”?Thread.Sleep
,System.Windows.Forms.Timer
和Monitor.Wait
。但我似乎无法弄清楚如何等待X秒然后执行其他操作 - 而不锁定线程。Thread.Sleep
会导致整个应用程序在5秒钟内无响应 - 那么我该如何“等待5秒钟后执行某些操作”?(从Ben的评论中转录)
只需使用System.Windows.Forms.Timer。将计时器设置为5秒,并处理Tick事件。当事件触发时,执行该操作。
...并在执行工作之前禁用计时器(IsEnabled=false),以抑制第二次触发。
Tick事件可能在无法修改您的gui的另一个线程上执行,您可以捕获它:
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void StartAsyncTimedWork()
{
myTimer.Interval = 5000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
/* Not on UI thread, reenter there... */
this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
}
else
{
lock (myTimer)
{
/* only work when this is no reentry while we are already working */
if (this.myTimer.Enabled)
{
this.myTimer.Stop();
this.doMyDelayedWork();
this.myTimer.Start(); /* optionally restart for periodic work */
}
}
}
}
仅为完整性考虑:使用async/await,可以非常简单地延迟执行某些操作(一次性的,不需要重复调用):
private async Task delayedWork()
{
await Task.Delay(5000);
this.doMyDelayedWork();
}
//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
Task ignoredAwaitableResult = this.delayedWork();
}
更多信息请参见MSDN中的“async和await”。
更完整的内容:
根据您的框架,您很有可能拥有可以在内部处理调用的DispatcherTimer
类(WPF变体)。 (在ms文档中找到详细信息)
你尝试过吗?
public static Task Delay(
int millisecondsDelay
)
你可以像这样使用:
await Task.Delay(5000);
参考资料:https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx
Task.Factory.StartNew(()=>
{
Thread.Sleep(5000);
form.Invoke(new Action(()=>DoSomething()));
});
[编辑]
要传递区间,只需将其存储在变量中:
int interval = 5000;
Task.Factory.StartNew(()=>
{
Thread.Sleep(interval);
form.Invoke(new Action(()=>DoSomething()));
});
[/编辑]
您可以按照自己希望的方式等待UI线程。
Task.Factory.StartNew(async() =>
{
await Task.Delay(2000);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
Task.Run
代替 Task.Factory.StartNew
。int millisecondsDelay = 2000;
Task.Run(async() =>
{
await Task.Delay(millisecondsDelay);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
你的应用程序挂起是因为在主UI线程上调用了5秒的sleep/wait。将sleep/wait/任何操作放在单独的线程中(实际上System.Windows.Forms.Timer应该可以为您完成此操作),当它完成时,调用将某个控件变为绿色的操作。记得检查InvokeRequired。这里是一个简短的示例(SetText可以从另一个线程调用,如果是这样,调用将在文本框所在的主UI线程上调用):
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
我从这里获取了样例(非常值得一读!)。
@eFloh在被标记为答案的帖子中说:
Tick事件可能在另一个无法修改GUI的线程上执行,您可以捕获它...
那不是文档所说的。
您在示例代码中使用了System.Windows.Forms.Timer。
那是一个Forms.Timer。
根据C#文档,计时器事件在UI线程上引发。
此Windows计时器设计用于单线程环境,其中使用UI线程执行处理。它要求用户代码有一个可用的UI消息泵,并始终从同一线程操作...
你的理解方式有误。
点击按钮会启动一个定时器,时间间隔为x秒。当时间到达时,它的事件处理程序会执行任务。
那么你不想发生什么。
在x秒内经过了多久?
在任务执行期间?
例如,如果你不希望在延迟和任务完成之前单击该按钮,请在按钮单击处理程序中将其禁用,并在任务完成时启用它。
如果你只想要一个五秒的延迟,然后执行该任务,那么你应该将起始延迟传递给任务,让它来处理。
System.Timers
... 我认为你不需要为你所尝试的操作设置 CWND 计时器:请参阅 http://msdn.microsoft.com/en-us/library/0tcs6ww8(v=VS.90).aspx 以获取示例。它应该可以解决问题。 - Ahmed Masud