如何在功能之间等待

时间:2013-02-24 18:32:08

标签: c# .net multithreading wait

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

2 个答案:

答案 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();
  }

同时确保您始终使用尝试最终语句或其他体操来重新启用按钮,无论调用顺序是否崩溃。

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