2010-06-28 4 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

이상합니다. 스레드를 사용하지 마십시오. –

+0

@ 한스 패 탄트 : 어떻게 이런 종류의 코드를 설명하겠습니까? 'OnRun'의 내부에서 나는 그래프 관련 알고리즘을 실행하고이 순간에 무슨 일이 일어나고 있는지에 대한 사용자 설명을 보여주고 싶습니다.메시지와 꼭지점/가장자리 채색. 그런 일을 작은 조각으로 나누면 코드 작성이 힘들어집니다. – prostynick

+0

질문에 대한 대답은 Windows Forms 용 UI 스레딩 모델에 익숙하지 않은 것처럼 보입니다. 어떤 별도의 백그라운드 스레드로 "대체"해야하는 이벤트로 수행 할 수없는 작업을 설명해야합니다. – David

답변

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

+0

@prostynick - 문제 없습니다, 행운을 빕니다. – ChaosPandion

1

"...이 ... GUI 응용 프로그램이 될 것"

그때는 아마하지 않고 Main()에서 위와 같이 순차적 인 코드가없는 것입니다.

e.e. 기본 GUI 스레드는 위와 같은 직렬 코드를 실행하지 않지만 일반적으로 유휴 상태이거나 다시 그리기 등의 작업을 수행하거나 Continue 단추 클릭을 처리합니다.
그 이벤트 핸들러에서 Auto|ManualResetEvent을 사용하여 작업자가 계속 진행하도록 알릴 수 있습니다.
작업자에서 이벤트를 기다리십시오.

+0

감사합니다. 이것은 매우 명백합니다. 그러나 @Hans Passant가 지적했듯이, 이것은 약간 이상합니다. 어떻게 든 저는 단지'AutoResetEvent' 만 사용하려고 생각하지 않았습니다. – prostynick

+0

@prostynick : 여러분 환영합니다. –

0

나는 때때로 Wait 경우 제대로 작동 할 수 있도록 한 Monitor.Wait()를 사용하여 고려 언제든지, 하나의 코드를 작성해야한다고 제안 그것이 마치 맥박을받은 것처럼 자발적으로 행동했습니다. 일반적으로이 하나의 패턴 사용해야 의미

turn 그 사이에서 변경하는 것은 불가능하다
lock(monitorObj) 
{ 
    turn = [[identifier for this "thread"]]; 
    Monitor.PulseAll(monitorObj); 
    while(turn != [[identifier for this "thread"]]) 
    Monitor.Wait(monitorObj); 
} 

그것의 여부를 확인중인 : 당신의 시나리오

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

를, 내가 좋아하는 일을하고 좋을 것 현재 스레드의 진행을 계속하고 Monitor.Wait. 따라서 Wait을 건너 뛰지 않으면 PulseAll이이를 깨울 수 있습니다. Wait이 자발적으로 펄스를받은 것처럼 행동하면 코드가 정상적으로 작동합니다. 단지 회전 할 것이고 현재 스레드에 대해 turn이 설정되지 않았 음을 관찰하고 대기 상태로 되돌아갑니다.

관련 문제