我想在不使用Thread.sleep的情况下执行以下操作。基本上我必须调用3个函数,每个函数之间有一些等待时间,并在循环中运行整个事件100次。
我尝试使用Timer类,但似乎我使用的不正确。
func test()
for(int i=0;i<100;i++){
func A()
wait for 20 seconds
func B()
wait for 45 seconds
func C()
wait for 2 mins
}//repeat for 100 times
答案 0 :(得分:2)
您可能正在寻找任务框架。你可以这样做:
async void Run()
{
for (int i=0; i<100; i++)
{
A();
await Task.Delay(TimeSpan.FromSeconds(20));
B();
await Task.Delay(TimeSpan.FromSeconds(45));
C();
await Task.Delay(TimeSpan.FromMinutes(2));
}
}
该函数将立即返回,但该循环将继续在后台运行。
编辑:正如下面提到的svick,这是一个新的.NET 4.5功能。
答案 1 :(得分:0)
我要去做一个很大的假设(所有线索都指向那个方向):
我假设您在Windows窗体中执行所有这些操作,并且您希望GUI在Thread.Sleep()调用期间不会冻结,对吧?如果是这种情况,那么你可以编写这个方法:
public static class Foo {
public static void MySleep(int milliseconds) {
int startTick = Environment.TickCount;
while (Environment.TickCount - startTick < milliseconds)
Application.DoEvents();
}
}
然后用Foo.MySleep调用替换Thread.Sleep调用(但是 BEWARE 了解Application.DoEvents的含义,它允许整个过程在控件内部时重新执行任何MySleep调用。如果你不采取预防措施,我不推荐以这种方式做事 - 例如你应该总是禁用调用 func test() 测试正在执行,因为它可以在永无止境的递归中反复执行):
func test()
for(int i=0;i<100;i++){
func A()
Foo.MySleep(20 * 1000);
func B()
Foo.MySleep(45 * 1000);
func C()
Foo.MySleep(2 * 60 * 1000);
}//repeat for 100 times
还有另一种方法(有点安全)做到这一点。 它涉及一个辅助线程和一些Control.Invoke调用。它还允许您使用标准的System.Thread.Sleep(int milliseconds)方法。
它是这样的:
public class Form1 : Form {
public void triggerTestAndReturnImmediately() {
new Thread(start: () => {
Action callA = () => funcA();
Action callB = () => funcB();
Action callC = () => funcC();
for(int i=0;i<100;i++){
this.Invoke(callA);
Thread.Sleep(20 * 1000);
this.Invoke(callB);
Thread.Sleep(45 * 1000);
this.Invoke(callC);
Thread.Sleep(2 * 60 * 1000);
}//repeat for 100 times
}).Start();
}
}
“this.Invoke(someDelegate)”位基于以下事实:“triggerTestAndReturnImmediately”方法是Form子类的成员,“this”是Form实例。
这里发生的是你在一个单独的线程上运行等待操作+触发实际工作者(funcA,funcB,funcC)。为什么你只运行实际工人的工作而不是实际的工人呢?因为您最有可能从这些函数访问GUI对象,而WinForms(以及许多其他.NET或非.NET GUI框架)中不允许这样做。
所以你问GUI线程(正如你在问题中指出的那样)必须在99%的时间内自由执行这些函数 - 因此this.Invoke(someDelegate)调用。
您应该采取我之前提到的相同预防措施,但这取决于您和您希望您的应用程序做什么。假设您希望按钮的Click事件处理程序调用 triggerTestAndReturnImmediately ,并且您希望在此过程中禁用它,您将如何禁用和重新启用按钮?
如果你这样做:
void buttonBar_Click(object sender, EventArgs e) {
this.buttonBar.Enabled = false;
this.triggerTestAndReturnImmediately();
this.buttonBar.Enabled = true;
}
然后这对你没有任何好处,因为 triggerTestAndReturnImmediately 方法正如它的名字所暗示的那样:“它立即返回”。
那你怎么把它拉掉呢?你可以这样做:
void buttonBar_Click(object sender, EventArgs e) {
this.triggerTestAndReturnImmediately();
}
public void triggerTestAndReturnImmediately() {
this.buttonBar.Enabled = false;
new Thread(start: () => {
Action callA = () => funcA();
Action callB = () => funcB();
Action callC = () => funcC();
Action reenableButton = () => this.buttonBar.Enabled = true;
for(int i=0;i<100;i++){
this.Invoke(callA);
Thread.Sleep(20 * 1000);
this.Invoke(callB);
Thread.Sleep(45 * 1000);
this.Invoke(callC);
Thread.Sleep(2 * 60 * 1000);
this.Invoke(reenableButton);
}//repeat for 100 times
}).Start();
}
同时确保您始终使用尝试最终语句或其他体操来重新启用按钮,无论调用顺序是否崩溃。
所以我认为你可以了解我所说的预防措施是什么(它们取决于你希望你的应用做什么)。