>

코드는 다음과 같이 간단합니다 :

package main
import (
        "fmt"
        //      "sync"
        "time"
)
var count = uint64(0)
//var l sync.Mutex
func add() {
        for {
                //              l.Lock()
                //              fmt.Println("Start ++")
                count++
                //              l.Unlock()
        }
}
func main() {
        go add()
        time.Sleep(1 * time.Second)
        fmt.Println("Count =", count)
}

사례 :

<올>
  • 변경하지 않고 코드를 실행하면 "Count = 0"이됩니다. 예상되지 않음?
  • 16 주석 해제 만 "fmt.Println ("Start ++ ")";u는 "Start ++"가 많고 "Count = 11111"과 같이 Count가있는 값이 출력됩니다. 예상?
  • 11 행 "var l sync.Mutex", 15 행 "l.Lock ()"및 18 행 "l.Unlock ()"의 주석 처리를 제거하고 16 행은 주석 처리하십시오. u는 "Count = 111111111"과 같은 출력을 얻습니다. 기대합니다.
  • 그래서 ... 공유 변수 사용에 문제가 생겼나요? 내 질문 :

    <올>
  • 사례 1에 Count가 0 인 이유는 무엇입니까?
  • 사례 1이 예상된다면 왜 사례 2가 발생 했습니까?
  • 봉투 : 1. go1.8 linux/amd64 버전으로 이동 2. 3.10.0-123.el7.x86_64 3. CentOS Linux 릴리스 7.0.1406 (코어)

    • 답변 # 1

      동기화 없이는 전혀 보장 할 수 없습니다.

      첫 번째 경우에 'Count = 0'이 표시되는 이유는 여러 가지가 있습니다.

      <올>

      멀티 프로세서 또는 멀티 코어 시스템이 있고 하나의 장치 (cpu 또는 코어)가 for 루프에서 즐겁게 떨리는 반면, 다른 장치는 1 초 동안 휴면 상태를 유지하고 나중에보고있는 회선을 인쇄합니다. 컴파일러가 머신 코드를 생성하는 것은 완전히 합법적입니다.이 코드는 값을 일부 레지스터에로드하고 for 루프에서 해당 레지스터 만 증가시킵니다. 변수를 사용하여 기능을 수행하면 메모리 위치를 업데이트 할 수 있습니다. 무한 for 루프의 경우 결코 그렇지 않습니다. 프로그래머는 컴파일러에게 동기화를 생략하여 해당 변수에 대한 경합이 없다고 말했다.

      뮤텍스 버전에서 동기화 프리미티브는 컴파일러에게 뮤텍스를 취하는 다른 스레드가있을 수 있으므로 뮤텍스를 잠금 해제하기 전에 레지스터에서 메모리 위치로 값을 다시 써야합니다. 적어도 그렇게 생각할 수 있습니다. 잠금 해제 및 이후 잠금 조작이 두 go 루틴 사이의 관계 이전에 발생하고 실제로는 다른 스레드에서 잠금 조작 후 한 스레드에서 잠금 해제 전의 변수에 대한 모든 쓰기를 볼 수 있음을 보장합니다. go 메모리 모델 잠금에 설명 된대로 이것이 구현되는 방식에 따라 다릅니다.

      기본 실행 루틴에서 휴면이 완료 될 때까지 Go 런타임 스케줄러가 for 루프를 전혀 실행하지 않습니다. (아마도 그렇지는 않지만, 정확하게 기억한다면, 이것이 일어나지 않을 것이라는 보장은 없습니다.) 슬프게도 스케줄러가 어떻게 작동하는지에 대한 공식 문서는 많지 않지만 특정 일정에 따라 고 루틴 만 예약 할 수 있습니다 포인트, 그것은 선제 적이지는 않습니다. 이것의 결과는 심각하다. 예를 들어, 코어가있는만큼 많은 루틴을 실행하여 변수를 증분하는 무한 루프를 수행하여 일부 버전의 go에서 프로그램을 영원히 실행할 수 있습니다. 메인 go 루틴 (프로그램을 종료 할 수 있음)에 대한 코어가 남아 있지 않았으며 스케줄러는 변수 증가와 같은 간단한 작업 만 수행하는 무한 for 루프에서 go 루틴을 선점 할 수 없습니다. 지금 변경되었는지 모르겠습니다.

      다른 사람들이 지적했듯이, 그것은 데이터 레이스입니다. 구글과 그것에 대해 읽어보십시오.

      16 번째 줄만 주석 처리/코멘트 해제 된 버전 간의 차이점은 터미널 인쇄 속도가 매우 느릴 수 있기 때문에 런타임 때문일 가능성이 높습니다.

      올바른 프로그램을 위해서는 주 프로그램에서 수면 후와 fmt.Println 전에 뮤텍스를 추가로 잠그고 나중에 잠금을 해제해야합니다. 그러나 결과는 machine/os /에 따라 달라질 수 있기 때문에 출력에 대한 결정 론적 기대는 없습니다 ...

    • 답변 # 2

      count 에서 데이터 경쟁이 있습니다 . 결과는 정의되어 있지 않습니다.

      package main
      import (
          "fmt"
          //      "sync"
          "time"
      )
      var count = uint64(0)
      //var l sync.Mutex
      func add() {
          for {
              //              l.Lock()
              //              fmt.Println("Start ++")
              count++
              //              l.Unlock()
          }
      }
      func main() {
          go add()
          time.Sleep(1 * time.Second)
          fmt.Println("Count =", count)
      }
      
      

      출력 :

      $ go run -race racer.go
      ==================
      WARNING: DATA RACE
      Read at 0x0000005995b8 by main goroutine:
        runtime.convT2E64()
            /home/peter/go/src/runtime/iface.go:255 +0x0
        main.main()
            /home/peter/gopath/src/so/racer.go:25 +0xb9
      Previous write at 0x0000005995b8 by goroutine 6:
        main.add()
            /home/peter/gopath/src/so/racer.go:17 +0x5c
      Goroutine 6 (running) created at:
        main.main()
            /home/peter/gopath/src/so/racer.go:23 +0x46
      ==================
      Count = 42104672
      Found 1 data race(s)
      $
      
      

      참조 :

      유명한 데이터 경쟁 : 무엇이 잘못 될 수 있습니까?

  • 이전 roaming profile - Xamarin을 사용하여 장치간에 사용자 기본 설정을 공유하는 방법
  • 다음 oracle - Visual Studio 2013 용 ODAC