2017-10-04 1 views
4

나는 근근이 살아가고있는 URL 목록을 가지고 있습니다. 내가 뭘하고 싶은지는 성공적으로 긁힌 페이지 데이터를 모두 채널에 저장하고 완료되면 슬라이스에 덤프합니다. 얼마나 많은 성공적인 가져 오기를 얻을지 모르겠으므로 고정 길이를 지정할 수 없습니다. 코드가 wg.Wait()에 도달하고 모든 wg.Done() 메서드가 호출 될 때까지 기다렸지만 close(queue) 문에 도달하지 못했습니다. 내 wg.Wait() goroutine 내부가, close(queue)에 도달 한 포장 자마자goroutine에서 wg.Wait()를 실행할 때 왜 코드가 올바르게 작동합니까?

ports := make(chan string) 
toScan := make(chan int) 
var wg sync.WaitGroup 

// make 100 workers for dialing 
for i := 0; i < 100; i++ { 
    wg.Add(1) 
    go func() { 
     defer wg.Done() 
     for p := range toScan { 
      ports <- worker(*host, p) 
     } 
    }() 
} 

// close our receiving ports channel once all workers are done 
go func() { 
    wg.Wait() 
    close(ports) 
}() 

을 : 비슷한 대답을 찾고, 나는 저자가 비슷한 않는 경우 SO

https://stackoverflow.com/a/31573574/5721702

대답이 건너 온 : 내가 AG를 사용하지 않는 경우

urls := getListOfURLS() 
activities := make([]Activity, 0, limit) 
queue := make(chan Activity) 
for i, activityURL := range urls { 
    wg.Add(1) 
    go func(i int, url string) { 
     defer wg.Done() 
     activity, err := extractDetail(url) 
     if err != nil { 
      log.Println(err) 
      return 
     } 
     queue <- activity 
    }(i, activityURL) 
} 
    // calling it like this without the goroutine causes the execution to hang 
// wg.Wait() 
// close(queue) 

    // calling it like this successfully waits 
go func() { 
    wg.Wait() 
    close(queue) 
}() 
for a := range queue { 
    // block channel until valid url is added to queue 
    // once all are added, close it 
    activities = append(activities, a) 
} 

왜 코드는 close에 도달하지 않는다 wg.Wait()에 대한 oroutine? 나는 wg.Wait()에 도착하기 때문에 모든 defer wg.Done() 문장이 호출되어 결국 정리할 것이라고 생각합니다. 내 채널에서 값을받는 것과 관련이 있습니까?

+0

전체 코드를 표시 할 수 있습니까? –

+0

기본적으로 채널은 버퍼링되지 않습니다. 즉, 전송 된 값을 수신 할 준비가되어있는 해당 수신자가있는 경우에만 전송을 허용합니다. 대기열로 활동을 보내면 그에 상응하는 수신이 차단되고 차단됩니다. 모든 goroutines 마지막으로 wg.wait가 중단됩니다. 두 개 이상의 입력을 버퍼링하는 데 사용할 수있는 버퍼링 된 채널이라는 개념이 있습니다. https://gobyexample.com/channel-buffering –

답변

4

queue을 읽을 필요가 있기 때문에 goroutines가 별도의 스레드에서 완료 될 때까지 기다려야합니다. 당신은 다음 작업을 수행 할 때 : queue 이후 queue <- activity에서

queue := make(chan Activity) 
for i, activityURL := range urls { 
    wg.Add(1) 
    go func(i int, url string) { 
     defer wg.Done() 
     activity, err := extractDetail(url) 
     if err != nil { 
      log.Println(err) 
      return 
     } 
     queue <- activity // nothing is reading data from queue. 
    }(i, activityURL) 
} 

wg.Wait() 
close(queue) 

for a := range queue { 
    activities = append(activities, a) 
} 

각 goroutine 블록 버퍼링하고 아무것도에서 데이터를 읽고되지 않습니다. 이는 범위 루프가 queue 일 때 wg.Wait 이후 주 스레드에 있기 때문입니다.

wg.Wait은 모든 고 루틴이 반환 된 후에 만 ​​차단을 해제합니다. 그러나 앞서 언급했듯이 모든 goroutine은 채널 전송시 차단됩니다.

기다리는 데 별도의 goroutine을 사용하면 실제로 코드 실행이 queue의 범위 루프에 도달합니다.

// wg.Wait does not block the main thread. 
go func() { 
    wg.Wait() 
    close(queue) 
}() 

이것은 (queue을 읽기 시작 메인 스레드)와 완료 될 때까지 실행 queue <- activity 문에서 차단 해제 goroutines 발생합니다. 각 개인을 차례로 호출합니다. wg.Done.

대기중인 goroutine이 wg.Wait을 지나면 queue이 닫히고 주 스레드가 범위 루프를 종료합니다.

+0

을 통해 이동하십시오. 설명을 이해하면되지만 읽은 후에 만하면 , 나는 그것을 쓰는 가장 좋은 방법이라고 쓴 방법인가? 나는 그런 식으로 해결할 논리를 의도하지 않았기 때문에 코드가 다소 복잡하고 너무 읽기 쉽지 않을까 걱정됩니다. –

+0

이것은 이와 같은 문제가 어떻게 처리되는지를 보여줍니다. 어떤 사람들은 독점적으로 채널에 의존하는 것을 선호하지만 대기 그룹에는 아무런 문제가 없습니다. 가독성 측면에서 코드의 흐름을 이해하는 데 문제가있을 수 있습니다. 그리고 그 언어로 더 많은 작업을 할 때 편안함을 느낄 수 있습니다. – abhink

1

queue 채널은 버퍼링되지 않으므로, 읽기 프로세스가 아직 시작되지 않았기 때문에 채널에 쓰려고하는 모든 goroutine이 차단됩니다. 따라서 어떤 goroutinte도 쓸 수없고 모두 끊어집니다. 결과적으로 wg.Wait은 영원히 기다립니다. 별도의 goroutine에 독자를 시작하려고 :

go func() { 
    for a := range queue { 
     // block channel until valid url is added to queue 
     // once all are added, close it 
     activities = append(activities, a) 
    } 
}() 

을 한 다음 시작 웨이터 :

wg.Wait() 
close(queue) 

당신이 채널에있는 모든 데이터를 축적하고 과부하하지만, 같은 데이터를 얻을 수없는이 방법 와서 대상 슬라이스에 놓습니다.

+0

당신은 goroutine에서 리더를 먼저 호출 한 다음 goroutine없이 웨이터에게 전화하는 것을 의미합니까? 나는 이것을 시도하고 예상했던 4 대신에 3 개의 결과를 반환했다. 나는'wg.Done()'에 도달하고 슬라이스가 반환되기 전에 너무 일찍 큐를 닫아야한다고 생각한다. 마지막 항목을 추가 할 수 있습니다. –

+0

예. 이 경우 리더가 끝날 때까지 기다릴 필요가 있습니다. 그렇지 않으면 마지막 레코드가 손실 될 수 있습니다. –

관련 문제