이 코드는 호출 된 실행 파일로 동일한 폴더에있는 모든 xml 파일을 선택하고 콜백 메서드의 각 결과에 비동기 적으로 처리를 적용합니다 (아래 예에서는 파일 이름 만 인쇄 됨).
주 메서드가 종료되지 않도록하려면 sleep 메서드를 사용하지 않으려면 어떻게해야합니까? 채널 주위에 머리를 감는 데 문제가 있으므로 (결과를 동기화하는 데 필요한 것이라고 가정합니다) 도움을 주시면 감사하겠습니다!
package main
import (
"fmt"
"io/ioutil"
"path"
"path/filepath"
"os"
"runtime"
"time"
)
func eachFile(extension string, callback func(file string)) {
exeDir := filepath.Dir(os.Args[0])
files, _ := ioutil.ReadDir(exeDir)
for _, f := range files {
fileName := f.Name()
if extension == path.Ext(fileName) {
go callback(fileName)
}
}
}
func main() {
maxProcs := runtime.NumCPU()
runtime.GOMAXPROCS(maxProcs)
eachFile(".xml", func(fileName string) {
// Custom logic goes in here
fmt.Println(fileName)
})
// This is what i want to get rid of
time.Sleep(100 * time.Millisecond)
}
답변
sync.WaitGroup 을 사용할 수 있습니다 . 링크 된 예를 인용하면 :
package main
import (
"net/http"
"sync"
)
func main() {
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Decrement the counter when the goroutine completes.
defer wg.Done()
// Fetch the URL.
http.Get(url)
}(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()
}
답변
WaitGroups는 확실히이를 수행하는 표준 방법입니다. 하지만 완전성을 위해 WaitGroups가 도입되기 전에 일반적으로 사용 된 솔루션은 다음과 같습니다. 기본 아이디어는 채널을 사용하여 “완료”라고 말하고 생성 된 각 루틴이 완료를보고 할 때까지 메인 고 루틴이 대기하도록하는 것입니다.
func main() {
c := make(chan struct{}) // We don't need any data to be passed, so use an empty struct
for i := 0; i < 100; i++ {
go func() {
doSomething()
c <- struct{}{} // signal that the routine has completed
}()
}
// Since we spawned 100 routines, receive 100 messages.
for i := 0; i < 100; i++ {
<- c
}
}
답변
sync.WaitGroup 이 여기에서 도움을 드릴 수 있습니다.
package main
import (
"fmt"
"sync"
"time"
)
func wait(seconds int, wg * sync.WaitGroup) {
defer wg.Done()
time.Sleep(time.Duration(seconds) * time.Second)
fmt.Println("Slept ", seconds, " seconds ..")
}
func main() {
var wg sync.WaitGroup
for i := 0; i <= 5; i++ {
wg.Add(1)
go wait(i, &wg)
}
wg.Wait()
}
답변
sync.waitGroup
(wg)는 표준 전달 방법 이지만 모두 완료 wg.Add
하려면 먼저 호출 중 일부를 수행해야합니다 wg.Wait
. 이는 웹 크롤러와 같은 간단한 작업에서는 가능하지 않을 수 있습니다. 웹 크롤러는 미리 재귀 호출 수를 모르고 호출을 유도하는 데이터를 검색하는 데 시간이 걸립니다 wg.Add
. 결국 첫 번째 하위 페이지 배치의 크기를 알기 전에 첫 번째 페이지를로드하고 구문 분석해야합니다.
waitGroup
내 솔루션에서 Tour of Go-웹 크롤러 연습을 피하면서 채널을 사용하여 솔루션을 작성했습니다 . 하나 이상의 go-routine이 시작될 때마다 번호를 children
채널로 보냅니다 . 이동 루틴이 완료 되려고 할 1
때마다 done
채널에 를 보냅니다 . 자녀의 합이 완료의 합과 같으면 완료된 것입니다.
내 유일한 관심사는 results
채널 의 하드 코딩 된 크기 이지만 (현재) Go 제한 사항입니다.
// recursionController is a data structure with three channels to control our Crawl recursion.
// Tried to use sync.waitGroup in a previous version, but I was unhappy with the mandatory sleep.
// The idea is to have three channels, counting the outstanding calls (children), completed calls
// (done) and results (results). Once outstanding calls == completed calls we are done (if you are
// sufficiently careful to signal any new children before closing your current one, as you may be the last one).
//
type recursionController struct {
results chan string
children chan int
done chan int
}
// instead of instantiating one instance, as we did above, use a more idiomatic Go solution
func NewRecursionController() recursionController {
// we buffer results to 1000, so we cannot crawl more pages than that.
return recursionController{make(chan string, 1000), make(chan int), make(chan int)}
}
// recursionController.Add: convenience function to add children to controller (similar to waitGroup)
func (rc recursionController) Add(children int) {
rc.children <- children
}
// recursionController.Done: convenience function to remove a child from controller (similar to waitGroup)
func (rc recursionController) Done() {
rc.done <- 1
}
// recursionController.Wait will wait until all children are done
func (rc recursionController) Wait() {
fmt.Println("Controller waiting...")
var children, done int
for {
select {
case childrenDelta := <-rc.children:
children += childrenDelta
// fmt.Printf("children found %v total %v\n", childrenDelta, children)
case <-rc.done:
done += 1
// fmt.Println("done found", done)
default:
if done > 0 && children == done {
fmt.Printf("Controller exiting, done = %v, children = %v\n", done, children)
close(rc.results)
return
}
}
}
}
답변
다음은 WaitGroup을 사용하는 솔루션입니다.
먼저 두 가지 유틸리티 메서드를 정의합니다.
package util
import (
"sync"
)
var allNodesWaitGroup sync.WaitGroup
func GoNode(f func()) {
allNodesWaitGroup.Add(1)
go func() {
defer allNodesWaitGroup.Done()
f()
}()
}
func WaitForAllNodes() {
allNodesWaitGroup.Wait()
}
그런 다음 다음 호출을 바꿉니다 callback
.
go callback(fileName)
유틸리티 함수를 호출하면 :
util.GoNode(func() { callback(fileName) })
마지막 단계로 . main
대신이 줄을 sleep
. 이렇게하면 프로그램이 중지되기 전에 주 스레드가 모든 루틴이 완료되기를 기다리고 있는지 확인할 수 있습니다.
func main() {
// ...
util.WaitForAllNodes()
}