2017-12-08 32 views
0

다음 Go 코드 예에서는 c <- byte(0)close(c) 사이의 경쟁 조건이 있습니다. 코드가 go test -race으로 실행되면 신호가 보내집니다.이동 채널을 닫을 때의 이동 조건

func TestRace(t *testing.T) { 
    var c = make(chan byte, 20) 
    go func() { 
     defer func() { 
      if r := recover(); r == nil { 
       t.Error("expected panic error") 
      } 
     }() 
     for i := 0; i < 25; i++ { 
      c <- byte(0) 
     } 
     t.Error("expected a panic") 
    }() 
    close(c) 
} 

어떻게하면이 경쟁 조건을 피할 수 있습니까?

편집 :

func TestRace(t *testing.T) { 
    var c = make(chan byte, 20) 
    var done = make(chan struct{}) 
    go func() { 
     for i := 0; i < 25; i++ { 
      select{ 
      case c <- byte(0): 
      case <-done: 
       close(c) 
       return 
     } 
    }() 
    close(done) 
} 

이 경쟁 조건이되지 않고 깨끗합니다 그의 의견에 Icza 제안에 따라, 여기에 솔루션입니다. 그것은 어리석은 간단한 예입니다. select가 오버 헤드를 추가한다고 들었지만, 사용 사례와 관련이 없기 때문에이를 조사하지 않았습니다.

+1

goroutine이 경쟁 조건을 제거하기 전에 실행되도록'close (c)'를 위로 이동하십시오. 어떤 이유로 든 채널을 닫고 그 채널에 글쓰기의 책임이 다른 경쟁자에 속해야한다면 경쟁 조건이 항상 존재할 수 있습니다. WaitGroups 및 다른 잠금을 사용하면 안전하게 수행 할 수 있습니다. – whytheplatypus

답변

4

일반적으로 채널에 값을 보내는 goroutine이이를 닫아야합니다. 채널을 닫는 것은 기본적으로 더 이상 값을 보낼 수 없다는 신호입니다.

당신은 이것을하지 않습니다 : 당신의 새로운 goroutine은 값을 보내는 것이고 다른 goroutine은 그것을 닫는 것입니다. 거기에 값을 보내는 goroutine, 예컨대 :

go func() { 
    defer func() { 
     if r := recover(); r == nil { 
      fmt.Println("expected panic error") 
     } 
    }() 
    for i := 0; i < 25; i++ { 
     c <- byte(0) 
    } 
    close(c) 
    fmt.Println("expected a panic") 
}() 
for x := range c { 
    fmt.Println("Received:", x) 
} 

Go Playground에 그것을 시도하는 close(c) 전화를 이동 :

그들이 의도 한대로 단순히 채널을 사용, 경쟁 조건을 제거하는.

+4

나는 더 멀리 가서 수신기가 채널을 닫는 것이 오류라고 말한다. [close] (https://golang.org/ref/spec#Close)가 수신 전용 채널에서 작동하지 않는 이유가 있습니다. –

+0

나는 플레이 그 라우드에서 경쟁 조건을 테스트하는 방법을 보지 못했습니다. 유스 케이스는 파이프 라인 처리를 중단하여 예를 들어 다시 시작하려는 컨트롤러입니다. 하나 이상의 데이터 소스가있을 수 있습니다. 채널을 닫는 소스가 무엇이든지 경쟁 조건을 만듭니다. 내 예는 차단 된 파이프 라인을 보여줍니다. Go에서 파이프 라인을 재설정 할 수 없다는 뜻입니까? – chmike

+0

@chmike 놀이터에서 "경주 테스트"를 할 수없고, 저장하고 경주 감지기를 로컬에서 실행할 수 없습니다. 소스를 여러 개 갖고 "컨트롤러"를 재설정하려면 소스와 컨트롤러를 조정하고 동기화해야합니다 (모든 경쟁 조건에 대한 일반적인 해결책 : 동기화). – icza