Este código seleciona todos os arquivos xml na mesma pasta, como o executável invocado e aplica de forma assíncrona o processamento a cada resultado no método de retorno de chamada (no exemplo abaixo, apenas o nome do arquivo é impresso).
Como evito usar o método sleep para impedir que o método principal saia? Tenho problemas para entender os canais (presumo que seja o que for preciso, para sincronizar os resultados), então qualquer ajuda é apreciada!
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)
}
go
synchronization
goroutine
Dante
fonte
fonte
Note that calls with positive delta must happen before the call to Wait, or else Wait may wait for too small a group. Typically this means the calls to Add should execute before the statement creating the goroutine or other event to be waited for. See the WaitGroup example.
wg := new(sync.WaitGroup)
vez devar wg sync.WaitGroup
.wg.Add(len(urls))
logo acima da linhafor _, url := range urls
, acredito que seja melhor já que você usa o Add apenas uma vez.go vet
detecta esse caso e avisa com" passes de função bloqueio por valor : sync.WaitGroup contém sync.noCopy ".WaitGroups são definitivamente a maneira canônica de fazer isso. No entanto, apenas por uma questão de integridade, aqui está a solução que era comumente usada antes da introdução dos WaitGroups. A ideia básica é usar um canal para dizer "terminei" e fazer a goroutine principal esperar até que cada rotina gerada tenha relatado sua conclusão.
fonte
doSomething()
retornar algum resultado, você pode colocá-lo no canal e coletar e processar os resultados no segundo loop for (assim que estiverem prontos)wg.Add(1)
-lo e, portanto, ele irá mantê-los sob controle. Com canais seria um pouco mais difícil.c
são diferentes do goroutine principal, que lê dec
. Assim, a goroutine principal está sempre disponível para ler um valor fora do canal, o que acontecerá quando uma das goroutines estiver disponível para gravar um valor no canal. Você está certo que se esse código não gerasse goroutines, mas, em vez disso, executasse tudo em uma única goroutine, haveria um deadlock.sync.WaitGroup pode ajudá-lo aqui.
fonte
Embora
sync.waitGroup
(wg) seja a maneira canônica de avançar, ele exige que você faça pelo menos algumas de suaswg.Add
chamadas anteswg.Wait
de todas serem concluídas. Isso pode não ser viável para coisas simples como um rastreador da web, onde você não sabe o número de chamadas recursivas de antemão e leva um tempo para recuperar os dados que conduzem owg.Add
chamadas. Afinal, você precisa carregar e analisar a primeira página antes de saber o tamanho do primeiro lote de páginas filhas.Eu escrevi uma solução usando canais, evitando
waitGroup
em minha solução o exercício Tour of Go - web crawler . Cada vez que uma ou mais rotinas de go são iniciadas, você envia o número para ochildren
canal. Cada vez que uma rotina go está prestes a ser concluída, você envia um1
para odone
canal. Quando a soma dos filhos for igual à soma de done, estamos prontos.Minha única preocupação restante é o tamanho do
results
canal embutido, mas essa é uma limitação do Go (atual).Código-fonte completo para a solução
fonte
Aqui está uma solução que emprega WaitGroup.
Primeiro, defina 2 métodos de utilidade:
Em seguida, substitua a invocação de
callback
:Com uma chamada para sua função de utilidade:
Última etapa, adicione esta linha no final de seu
main
, em vez de seusleep
. Isso garantirá que o thread principal esteja aguardando que todas as rotinas sejam concluídas antes que o programa possa ser interrompido.fonte