Este código selecciona todos los archivos xml en la misma carpeta, ya que el ejecutable invocado y aplica de forma asincrónica el procesamiento a cada resultado en el método de devolución de llamada (en el ejemplo siguiente, solo se imprime el nombre del archivo).
¿Cómo evito usar el método de dormir para evitar que salga el método principal? Tengo problemas para entender los canales (supongo que eso es lo que se necesita para sincronizar los resultados), ¡así que agradecemos cualquier ayuda!
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
fuente
fuente
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)
lugar devar wg sync.WaitGroup
.wg.Add(len(urls))
justo encima de la líneafor _, url := range urls
, creo que es mejor ya que usa Agregar solo una vez.go vet
detecta este caso y advierte con" func pasa bloqueo por valor : sync.WaitGroup contiene sync.noCopy ".WaitGroups es definitivamente la forma canónica de hacer esto. Sin embargo, solo en aras de la integridad, aquí está la solución que se usaba comúnmente antes de que se introdujeran WaitGroups. La idea básica es usar un canal para decir "He terminado" y hacer que la rutina principal espere hasta que cada rutina generada haya informado de su finalización.
fuente
doSomething()
devuelve algún resultado, puede ponerlo en el canal, y puede recopilar y procesar los resultados en el segundo ciclo for (tan pronto como estén listos)wg.Add(1)
y, por lo tanto, las rastreará. Con canales sería algo más complicado.c
son diferentes de la goroutine principal, que leec
. Por lo tanto, la goroutine principal siempre está disponible para leer un valor del canal, lo que sucederá cuando una de las goroutines esté disponible para escribir un valor en el canal. Tiene razón en que si este código no generara goroutines, sino que ejecutaba todo en una sola goroutine, se bloquearía.sync.WaitGroup puede ayudarlo aquí.
fuente
Aunque
sync.waitGroup
(wg) es la forma canónica a seguir, requiere que haga al menos algunas de suswg.Add
llamadas antes que ustedwg.Wait
para que todas las complete. Es posible que esto no sea factible para cosas simples como un rastreador web, donde no se conoce la cantidad de llamadas recursivas de antemano y lleva un tiempo recuperar los datos que impulsan laswg.Add
llamadas. Después de todo, debe cargar y analizar la primera página antes de saber el tamaño del primer lote de páginas secundarias.Escribí una solución utilizando canales, evitando
waitGroup
en mi solución el ejercicio Tour of Go - web crawler . Cada vez que se inician una o más rutinas, envía el número alchildren
canal. Cada vez que una rutina está a punto de completarse, envía un1
aldone
canal. Cuando la suma de los niños es igual a la suma de hecho, hemos terminado.Mi única preocupación restante es el tamaño codificado del
results
canal, pero esa es una limitación (actual) de Go.Código fuente completo de la solución
fuente
Aquí hay una solución que emplea WaitGroup.
Primero, defina 2 métodos de utilidad:
Luego, reemplace la invocación de
callback
:Con una llamada a su función de utilidad:
Último paso, agregue esta línea al final de su
main
, en lugar de susleep
. Esto asegurará que el hilo principal esté esperando a que finalicen todas las rutinas antes de que el programa pueda detenerse.fuente