views:

79

answers:

2

I would like to run code alternatively, so I could stop execution at any moment. Is this code safe?

static class Program
{
    static void Main()
    {
        var foo = new Foo();
        //wait for interaction (this will be GUI app, so eg. btnNext_click)
        foo.Continue();
        //wait again etc.
        foo.Continue();
        foo.Continue();
        foo.Continue();
        foo.Continue();
        foo.Continue();
    }
}

class Foo
{
    public Foo()
    {
        new Thread(Run).Start();
    }

    private void Run()
    {
        Break();
        OnRun();
    }

    protected virtual void OnRun()
    {
        for (var i = 0; i < 5; i++)
        {
            Console.WriteLine(i);
            Break();
        }
        //do something else and break;
    }

    private void Break()
    {
        lock (this)
        {
            Monitor.Pulse(this);
            Monitor.Wait(this);
        }
    }

    public void Continue()
    {
        lock (this)
        {
            Monitor.Pulse(this);
            Monitor.Wait(this);
        }
    }
}

Of course I know, that now the application will never ends, but that's not the point.

I need this, because I would like to present steps in some kind of an algorithm and describe what is going on in particular moment, and making everything in one thread would lead to many complications even when using small amount of loops in the code. For example those lines:

for (var i = 0; i < 5; i++)
{
    Console.WriteLine(i);
    Break();
}

should be then replaced with:

if (this.i < 5)
{
    Console.WriteLine(i++);
}

And that is just a small example of what I want to present. The code will be more complicated than a dummy for loop.

+1  A: 

"...this will be GUI app..."

Then you probably do not want and will not have sequential code like above in Main().

I.e. the main GUI thread will not execute a serial code like above, but generally be idle, repainting, etc. or handling the Continue button click.
In that event handler you may better use an Auto|ManualResetEvent to signal the worker to proceed.
In the worker, just wait for the event.

andras
Thank you. This is so obvious, but like @Hans Passant pointed out, this is a little bit weird, and somehow I haven't thought to use just `AutoResetEvent`.
prostynick
@prostynick: you are welcome.
andras
+1  A: 

I recommend you check out this blog post about implementing fibers.

Code (In case the site goes down.)

public class Fiber
{
    private readonly Stack<IEnumerator> stackFrame = new Stack<IEnumerator>();
    private IEnumerator currentRoutine;

    public Fiber(IEnumerator entryPoint)
    {
        this.currentRoutine = entryPoint;
    }

    public bool Step()
    {
        if (currentRoutine.MoveNext())
        {
            var subRoutine = currentRoutine.Current
                           as IEnumerator;
            if (subRoutine != null)
            {
                stackFrame.Push(currentRoutine);
                currentRoutine = subRoutine;
            }
        }
        else if (stackFrame.Count > 0)
        {
            currentRoutine = stackFrame.Pop();
        }
        else
        {
          OnFiberTerminated(
              new FiberTerminatedEventArgs(
                  currentRoutine.Current
              )
          );
          return false;
      }

      return true;
    }

    public event EventHandler<FiberTerminatedEventArgs> FiberTerminated;

    private void OnFiberTerminated(FiberTerminatedEventArgs e)
    {
        var handler = FiberTerminated;
        if (handler != null)
        {
            handler(this, e);
        }
    }
}

public class FiberTerminatedEventArgs : EventArgs
{
  private readonly object result;

  public FiberTerminatedEventArgs(object result)
  {
      this.result = result;
  }

  public object Result
  {
      get { return this.result; }
  }
}   

class FiberTest
{
  private static IEnumerator Recurse(int n)
  {
      Console.WriteLine(n);
      yield return n;
      if (n > 0)
      {
          yield return Recurse(n - 1);
      }
  }

  static void Main(string[] args)
  {
      var fiber = new Fiber(Recurse(5));
      while (fiber.Step()) ;
  }
}
ChaosPandion
Thank you very much for your link. This helps me a lot. I don't really need whole class, at least now. However, the idea of using `Iterator` and `yield` keyword to break (pause) execution is wonderful. Regards
prostynick
@prostynick - No problem, good luck with your work.
ChaosPandion