1

我想在不使用 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
4

2 回答 2

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 功能。

于 2013-02-24T19:24:42.043 回答
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 调用(但注意 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();
  }

还要确保您始终重新启用按钮,无论调用序列是否因某些try finally语句或其他体操而崩溃。

所以我认为您可以了解我所说的预防措施是什么(它们取决于您希望您的应用程序做什么)。

于 2013-02-24T19:24:02.093 回答