2013-07-23 2 views
2

를 부착 할 때 출력을 변경 I 다음 샘플 프로그램이 있습니다코드 디버거

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace StackoverflowExample 
{ 
    class Program 
    { 
     static int value = 1; 
     static void Main(string[] args) 
     { 
      Task t1 = Task.Run(() => 
      { 
       if (value == 1) 
       { 
        Thread.Sleep(1000); 
        value = 2; 
       } 
      }); 

      Task t2 = Task.Run(() => 
      { 
       value = 3; 
      }); 

      Task.WaitAll(t1, t2); 
      Console.WriteLine(value); 
      Console.ReadLine(); 
     } 
    } 
} 

I 출력 2이 코드를 기대합니다. 나는 t1 값이 1임을 알았고 잠시 잠시 t2 값을 3으로 설정 한 다음 t12으로 다시 변경했습니다.

이것은 Visual Studio에서 F5 키를 누르면 디버거가 연결될 때 발생하는 동작입니다. 그러나 디버거 (Visual Studio에서 Ctrl + F5)를 연결하지 않고이 프로그램을 실행하면 출력은 3입니다.

왜?

답변

1

t2t1보다 먼저 시작하므로 if (value == 1)이 false를 반환합니다.

스레드가 순서대로 시작한다는 보장은 없습니다.

+1

다른 방법은 주위 : (I 제대로 OP를 이해하는 경우) * * 디버거에서 실행하지 않을 때 T2는 T1 전에 시작합니다. –

+0

@MatthewWatson : 네 말이 맞아. 나는 그 질문을 잘못 읽었다. – SLaks

2

t1 이전에 t2를 실행할 수없는 이유는 없습니다. TPL 라이브러리를 사용하면 LIFO 스택에 먼저 푸시되는 작업이 더 쉬워집니다.

디버거는 다른 결과를 생성 할만큼 지연 시키거나 변경할 수 있습니다.

작업 및 스레딩이 종종 잘못된 방법 일 수 있습니다.

+0

내가 아는 한 TPL에서 각 스레드 풀 스레드는 자체 로컬 LIFO 큐를 가지며 글로벌 FIFO 큐도 있습니다. 메인 쓰레드가 쓰레드 풀 쓰레드가 아니기 때문에,'Task's는 모두 글로벌 FIFO 큐로 갈 것입니다. – svick

+0

흥미 롭습니다! 작업 스케줄러 간의 LIFO/FIFO 차이에 대해 알지 못했습니다. –

0

"Race Condition"이 무엇입니까? 당신은 불확실한 시간이 걸릴 수있는 두 가지 행동을 취하고, 누가 시작/끝나 느냐에 따라 다른 결과를 얻습니다.

이러한 상황을 피하려면 코드가 여러 스레드로 작업 할 때 적절한 위치에서 대기해야합니다.

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace StackoverflowExample 
{ 
    class Program 
    { 
     static AutoResetEvent sequenceLock = new AutoResetEvent(false); 
     static int value = 1; 
     static void Main(string[] args) 
     { 
      Task t1 = Task.Run(() => 
      { 
       if (value == 1) 
       { 
        sequenceLock.Set(); //lets t2 past the WaitOne() 
        Thread.Sleep(1000); 
        value = 2; 
       } 
      }); 

      Task t2 = Task.Run(() => 
      { 
       sequenceLock.WaitOne(); //Waits for t1 to set the flag. 
       value = 3; 
      }); 

      Task.WaitAll(t1, t2); 
      Console.WriteLine(value); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

TPL을 사용하려는 경우 'AutoResetEvent'를 사용하는 대신 TPL 스타일의 프로그래밍을 사용하는 것이 좋습니다. 이 경우 작업을 서로의 연속으로 설정하여 원하는 종속성을 나타냅니다. – Servy

+0

@Servy는 좋은 지적입니다. 필자는 원래 코드에 대한 최소한의 변경으로 동기화의 개념을 보여 주려고했습니다. 내가 이것을 한 방식은 이것이 최종 코드라면 이것을 처리하는 "올바른"방법이 결코 아니지만이 사이트에 대한 간단한 예제를 만들었고 실제 코드는이 코드를 실행하기 전후에 많은 것을 실행해야한다고 생각했습니다. 순서대로 발생해야하는 "동기화 지점". –