2010-06-28 83 views
1

我想交替运行代码,所以我可以在任何时候停止执行。这段代码安全吗?这是一种安全的方式来执行线程吗?

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循环更复杂。

+0

这很奇怪。只是不要使用线程。 – 2010-06-28 21:06:02

+0

@Hans Passant:你会怎么做这种......自我解释(?)代码?在'OnRun'内部,我想运行图形相关的算法,并显示用户在这一时刻发生的情况的解释。消息和顶点/边缘着色。将这样的东西拆分成小块将会很难编码。 – prostynick 2010-06-28 21:19:36

+0

您提到问题的方式使您看起来好像不熟悉Windows窗体的UI线程模型。你应该解释什么是不能做的事情,需要与一些单独的后台线程“交替”。 – David 2010-06-28 21:20:31

回答

1

我建议你看看这个blog post关于实施纤维。

代码(如果网站出现故障。)

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()) ; 
    } 
} 
+0

非常感谢您的链接。这对我有很大的帮助。至少现在我并不需要全班上课。然而,使用'Iterator'和'yield'关键字来中断(暂停)执行的想法非常棒。问候 – prostynick 2010-06-28 23:25:08

+0

@prostynick - 没问题,祝你工作顺利。 – ChaosPandion 2010-06-28 23:28:03

1

“......这将是GUI应用程序......”

那么你可能不希望,不会有连续的代码像上面Main()

I.e.主GUI线程不会执行上述的串行代码,但通常是空闲的,重新绘制等或处理按钮单击。
在该事件处理程序中,您最好使用Auto|ManualResetEvent来指示工作人员继续。
在工人,只是等待事件。

+0

谢谢。这是如此明显,但像@Hans Passant指出的那样,这有点奇怪,不知何故,我没有想过只用'AutoResetEvent'。 – prostynick 2010-06-28 23:22:01

+0

@prostynick:不客气。 – 2010-06-28 23:32:50

0

我会建议任何时候一个考虑使用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未设置为当前线程,并返回等待状态。

相关问题