Это безопасный способ альтернативного выполнения потоков? - PullRequest
1 голос
/ 29 июня 2010

Я хотел бы запустить код поочередно, чтобы я мог остановить выполнение в любой момент.Этот код безопасен?

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);
        }
    }
}

Конечно, я знаю, что теперь приложение никогда не закончится, но это не главное.

Мне это нужно, потому что я хотел бы представить шагив каком-то алгоритме и опишите, что происходит в конкретный момент, и создание всего в одном потоке приведет к множеству сложностей, даже если в коде используется небольшое количество циклов.Например, эти строки:

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

должны быть заменены на:

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

И это лишь небольшой пример того, что я хочу представить.Код будет более сложным, чем фиктивный цикл for.

Ответы [ 3 ]

1 голос
/ 29 июня 2010

Я рекомендую вам прочитать это сообщение в блоге о внедрении волокон.

Код (В случае, если сайт закрывается.)

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()) ;
  }
}
1 голос
/ 29 июня 2010

"... это будет приложение с графическим интерфейсом ..."

Тогда вы, вероятно, не хотите и не будете иметь последовательный код, как указано выше в Main().

т.е. основной поток графического интерфейса не будет выполнять последовательный код, как описано выше, но обычно будет простаивать, перекрашивать и т. д. или обрабатывать нажатие кнопки Continue.
В этом обработчике событий вы можете лучше использовать Auto|ManualResetEvent, чтобы дать сигнал работнику продолжить.
В рабочем, просто подожди события.

0 голосов
/ 12 февраля 2013

Я бы предложил, чтобы каждый раз, когда кто-то рассматривал возможность использования Monitor.Wait(), он должен писать код, чтобы он работал правильно, если бы Wait иногда самопроизвольно действовал так, как будто получил импульс.Как правило, это означает, что следует использовать шаблон:

lock(monitorObj)
{
  while(notYetReady)
    Monitor.Wait(monitorObj);
}

Для вашего сценария я бы предложил сделать что-то вроде:

lock(monitorObj)
{
  turn = [[identifier for this "thread"]];
  Monitor.PulseAll(monitorObj);
  while(turn != [[identifier for this "thread"]])
    Monitor.Wait(monitorObj);
}

Невозможно изменить turnмежду проверкой того, наступает ли очередь текущего потока и Monitor.Wait.Таким образом, если Wait не пропущен, PulseAll гарантированно пробудит его.Обратите внимание, что код работал бы просто отлично, если бы Wait спонтанно действовал так, как если бы он получил импульс - он просто вращался, наблюдал, что turn не был установлен для текущего потока, и возвращался к ожиданию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...