2009-08-22 4 views
5

나는 http://www.mono-project.com/ThreadsBeginnersGuide을 읽고 있습니다.C# 스레딩 : 경쟁 조건 예제

첫 번째 예는 다음과 같습니다 출력

public class FirstUnsyncThreads { 
    private int i = 0; 

    public static void Main (string[] args) { 
     FirstUnsyncThreads myThreads = new FirstUnsyncThreads(); 
    } 

    public FirstUnsyncThreads() { 
     // Creating our two threads. The ThreadStart delegate is points to 
     // the method being run in a new thread. 
     Thread firstRunner = new Thread (new ThreadStart (this.firstRun)); 
     Thread secondRunner = new Thread (new ThreadStart (this.secondRun)); 

     // Starting our two threads. Thread.Sleep(10) gives the first Thread 
     // 10 miliseconds more time. 
     firstRunner.Start(); 
     Thread.Sleep (10); 
     secondRunner.Start(); 
    } 

    // This method is being excecuted on the first thread. 
    public void firstRun() { 
     while(this.i < 10) { 
      Console.WriteLine ("First runner incrementing i from " + this.i + 
           " to " + ++this.i); 
      // This avoids that the first runner does all the work before 
      // the second one has even started. (Happens on high performance 
      // machines sometimes.) 
      Thread.Sleep (100); 
     } 
    } 

    // This method is being excecuted on the second thread. 
    public void secondRun() { 
     while(this.i < 10) { 
      Console.WriteLine ("Second runner incrementing i from " + this.i + 
           " to " + ++this.i); 
      Thread.Sleep (100); 
     } 
    } 
} 

:

First runner incrementing i from 0 to 1 
Second runner incrementing i from 1 to 2 
Second runner incrementing i from 3 to 4 
First runner incrementing i from 2 to 3 
Second runner incrementing i from 5 to 6 
First runner incrementing i from 4 to 5 
First runner incrementing i from 6 to 7 
Second runner incrementing i from 7 to 8 
Second runner incrementing i from 9 to 10 
First runner incrementing i from 8 to 9 

와우,이게 뭐죠? 불행히도, 기사의 설명은 나를 위해 부적절합니다. 뒤죽박죽 순서로 증분이 왜 발생했는지 설명해 주시겠습니까?

감사합니다.

+0

언급 된 기사의 저자는 출력이 "표시 될 수 있습니다"라고 말합니다. 이는 출력이 표시되지 않았 음을 증명할 수 없다는 점에서 사실입니다. 그러나 실제로는 First가 약 10ms의 헤드 스타트를 받았다면, "뒤죽박죽"이 발생하기 전에 꽤 많은 반복 동안 락 스텝 변경이 일어날 것으로 예상 할 수 있습니다. –

답변

2

여러 스레드가있는 경우 동기화가 필수적입니다. 이 경우 두 스레드가 모두 this.i에 읽고 쓰는 것을 볼 수 있지만 이러한 액세스를 동기화 할 때는 아무런 시도도하지 않습니다. 두 개 모두 동일한 메모리 영역을 동시에 수정하므로 뒤죽박죽 된 결과가 발생합니다. 수면 전화는 위험합니다. 이것은 확실한 버그로가는 접근법입니다. 스레드가 원래 10 밀리 초 단위로 항상 이동한다고 가정 할 수 없습니다.

간단히 말해, 동기화를 위해 절전 모드를 사용하지 말고 대신 어떤 종류의 스레드 동기화 기술 (예 : 잠금, 뮤텍스, 세마포)을 채택하십시오. 항상 당신의 필요를 채울 수있는 가장 가벼운 가능한 자물쇠를 사용하십시오 ....

유용한 정보는 Windows의 동시 프로그래밍 (Joe Duffy)의 저서입니다.

+0

스레드를 시도하고 동기화하는 데 Thread.Sleep()이 사용되지 않는다고 생각합니다. 그것은 증분이 관찰 가능하도록하기 위해 사용됩니다 (그렇지 않으면 콘솔에 단번에 나타납니다). 경쟁 조건이 거의 제로가 될 가능성을 줄이는 부작용이 있습니다. –

+0

예, 주석에 따르면, 실제로는 대략적인 동기화 기술로 사용되며 Start()에 대한 두 호출 사이의 10 ms 간격을 살펴보십시오. 다른 기술이 없으면 Sleep이 두 스레드 간의 동기화를 위장한 것 같습니다. – Francesco

+0

조 더피 (Joe Duffy)의 +1 책인 Windows Concurrency Bible –

0

Console.WriteLine (...)은 여러 스레드의 출력을 단일 스레드 콘솔에 쓰고 많은 스레드에서 하나의 스레드로 동기화하면 메시지가 순서가 어긋난다.

이 예제에서는 경쟁 조건을 만들려고했지만 실패한 경우를 가정합니다. 불행하게도 경쟁 조건이나 교착 상태와 같은 동시성 문제는 본질적으로 예측하고 재생하기가 어렵습니다. 몇 번 시도해보고 더 많은 스레드를 사용하도록 변경하고 각 스레드가 더 많은 시간 (예 : 100,000) 증가해야합니다. 그런 다음 최종 결과가 모든 증분 (경쟁 조건으로 인해 발생)의 합계와 같지 않을 수 있습니다. 나는의 (a 이중 코에) 이것을 실행하면 내가 기대 한 것으로

+0

Debug.WriteLine? –

+0

죄송합니다, Console.WriteLine. 수정 됨. –

2

, 내 출력은

First runner incrementing i from 0 to 1 
Second runner incrementing i from 1 to 2 
First runner incrementing i from 2 to 3 
Second runner incrementing i from 3 to 4 
First runner incrementing i from 4 to 5 
Second runner incrementing i from 5 to 6 
First runner incrementing i from 6 to 7 
Second runner incrementing i from 7 to 8 
First runner incrementing i from 8 to 9 
Second runner incrementing i from 9 to 10 

입니다. Sleep (100)을 실행하는 두 개의 루프를 실행 중입니다. 이는 경쟁 조건을 증명하는 데 매우 적합하지 않습니다.

코드에 경쟁 조건이 있지만 (VoteyDisciple이 설명하는 것처럼) 표면이 보이지 않을 가능성이 높습니다.

출력에서 ​​순서가 부족하다는 것을 설명 할 수는 없지만 (실제 출력입니까?) Console 클래스는 출력 호출을 동기화합니다.

Sleep() 호출을 생략하고 1000 회 (10 개 대신) 루프를 실행하면 두 명의 주자가 모두 554에서 555 또는 그 이상으로 증가하는 것을 볼 수 있습니다.

+0

방금 ​​기사의 내용을 복사했지만 프로그램의 초기 절전 모드를 삭제하고 스레드의 절전 모드를 20으로 낮추면 비슷한 뒤죽박죽 출력을 얻을 수있었습니다. – George

+0

맞습니다. 원인은 콘솔 동기화에 있습니다. –

3

나는 기사의 작가가 물건을 혼란스럽게 생각합니다.

VoteyDisciple은 정확합니다. ++i은 원자가 아니며 대상이 작업 중에 잠겨 있지 않지만 위에서 설명한 문제가 발생하지 않으면 경쟁 조건이 발생할 수 있습니다.경쟁 조건과 같을 것이다 ++ 연산자의 ++i 다음 내부 작업을 호출이 발생하면

는 : -

  1. 1 스레드가 값을 읽어 0
  2. 2 스레드 값 0
  3. 1 스레드 단위로 값을 읽어 ~ 1
  4. 두 번째 스레드가 1로 증가합니다.
  5. 첫 번째 스레드에서 값 1을 쓰십시오.
  6. 2 스레드 값을 기입 한

6의 연산 (3)의 순서는 점은 변수가 동일한 점진 결과 값 (x)을 가지는 경우, 판독 동작, (1)과 (2) 모두가 일어날 수 있다는 것, 중요하지 각 스레드가 x 및 y의 고유 한 값에 대해 증분을 수행하는 대신

이 다음과 같은 출력이 발생할 수 있습니다 : - 더 악화 될 것입니다 무엇

First runner incrementing i from 0 to 1 
Second runner incrementing i from 0 to 1 

는 않습니다 : -

  1. 1 스레드 값을 읽어 0
  2. 2 스레드 값 0
  3. 읽고
  4. 두 번째 스레드가 1로 증가합니다.
  5. 두 번째 스레드에서 값 1을 쓰십시오.
  6. 2 스레드가 값 1
  7. 2 스레드 증분 값 2
  8. 2 스레드가 값 2
  9. 첫번째 스레드 단위 값 1
  10. 첫번째 스레드가 값 1
  11. 2 스레드가 값 1
  12. 를 읽고 쓰고 읽기와 쓰기
  13. 2 스레드 단위 값은 2
  14. 2 스레드 값 2
를 기록

이 다음과 같은 출력이 발생할 수 있습니다 : - 등

First runner incrementing i from 0 to 1 
Second runner incrementing i from 0 to 1 
Second runner incrementing i from 1 to 2 
Second runner incrementing i from 1 to 2 

그리고있다.

또한 i를 읽고 Console.WriteLine 호와 연접 i++i++i 이후 수행 사이의 가능한 경쟁 상태가있다. 이 같은 출력이 발생할 수 있습니다 : -

First runner incrementing i from 0 to 1 
Second runner incrementing i from 1 to 3 
First runner incrementing i from 1 to 2 

작가는 콘솔 출력의 예측 불가능 발생할 수 있습니다 설명하고 i 변수에 대한 경쟁 조건과 아무 상관이있다 뒤죽박죽 된 콘솔 출력. ++i을 수행하는 동안 또는 i++i을 연결하는 동안 i을 잠글 경우이 동작이 변경되지 않습니다.