Esperando a que terminen las gorutas -- go campo con concurrency camp codereview Relacionados El problema

Waiting for goroutines to finish


3
vote

problema

Español

Tengo un programa que inicia un montón de gorutas, los alimenta datos y cuando no hay más datos, espera que terminen las goroutinas. Yo uso sync.waitgroup. No estoy seguro de que estoy usando correctamente, de manera idiomática o si hay una mejor solución.

Fondo

Mi código real lee un archivo de registro de espacio en disco y números de archivos respaldados, etc. para varios servidores. Los registros se entremezclan. De estos datos produce un informe de página web por servidor con gráficos, etc., funciona, pero creo que puedo separar más limpiamente las funciones si uso la concurrencia. Este es un programa de prueba antes de seguir adelante y refactor.

Código (72 líneas incluyendo datos)

  using System.ComponentModel;  class clViewModelBase : INotifyPropertyChanged {     public event PropertyChangedEventHandler PropertyChanged;      protected void OnPropertyChanged(string propertyName)     {         this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));     }      protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)     {         var handler = this.PropertyChanged;         if (handler != null)         {             handler(this, e);         }     } } 1  

Salida

  using System.ComponentModel;  class clViewModelBase : INotifyPropertyChanged {     public event PropertyChangedEventHandler PropertyChanged;      protected void OnPropertyChanged(string propertyName)     {         this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));     }      protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)     {         var handler = this.PropertyChanged;         if (handler != null)         {             handler(this, e);         }     } } 2  

Pregunta:

¿Puedo mejorar esto? ¿Hay una mejor manera de esperar o un uso más idiomático de los grupos de espera?

Original en ingles

I have a program that starts a bunch of goroutines, feeds them data and when there is no more data, waits for the goroutines to finish. I use sync.WaitGroup. I am unsure if I am using correctly, in an idiomatic way or if there is a better solution.

Background

My real code reads a log file of disk-space and numbers of files backed up etc for several servers. The records are intermingled. From this data it produces a web-page report per server with graphs etc. It works but I believe I can more cleanly separate the functions if I use concurrency. This is a test program before I go ahead and refactor.

Code (72 lines including data)

package main  import (     "fmt"     "strconv"     "strings"     "sync" )  func main() {     p := newPool()     defer p.Close()      for _, line := range strings.Split(logData, "\n") {         f := strings.Fields(line)         yr, _ := strconv.Atoi(f[0])         sub := f[1]         amt, _ := strconv.Atoi(f[2])         r := logRec{yr, sub, amt}         p.Report(r)     } }  type logRec struct {     when    int     subject string     amount  int }  type pool struct {     reporters map[string]chan logRec     wg        *sync.WaitGroup }  func newPool() pool {     rs := make(map[string]chan logRec)     var wg sync.WaitGroup     return pool{reporters: rs, wg: &wg} }  func (p *pool) Report(rec logRec) {     if _, ok := p.reporters[rec.subject]; !ok {         c := make(chan logRec)         p.wg.Add(1)         go func() {             defer p.wg.Done()             tot := 0             for r := range c {                 tot += r.amount             }             fmt.Printf("Total for %s is %d\n", rec.subject, tot)         }()         p.reporters[rec.subject] = c     }     p.reporters[rec.subject] <- rec }  func (p pool) Close() {     for _, c := range p.reporters {         close(c)     }      p.wg.Wait() }  const logData = `2018 apples 9 2018 oranges 5 2019 apples 27 2019 lemons 13 2019 oranges 2 2020 oranges 1 2020 apples 16 2020 lemons 3` 

Output

Total for oranges is 8 Total for lemons is 16 Total for apples is 52 

Question:

Can I improve this? Is there a better way to wait or a more idiomatic use of WaitGroups?

     

Lista de respuestas

1
 
vote
vote
La mejor respuesta
 

Sí, ya que está aquí para revisión de código:

  1. nunca ignore los errores: PascalCase8
  PascalCase9  
  1. Puede usar solo un private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 0 / private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 1 de esta manera:
  private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 2  
  1. Usted está sobre complicación de una función simple 99887766555443363 , no necesita canales aquí, solo un mapa hace el trabajo: es este simple :
  private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 4  
  1. por el bien de la concurrencia: solo necesita un mapa concurrente, nada más:
  private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 5  
  1. Ejemplo de tarea de carrera larga:
  private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 6  

Y Finalmente, si sus tareas terminarán con el tiempo, puede usar private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 7 :

  private clDelegateCommand _EditCommand; private clDelegateCommand _SaveCommand; private clDelegateCommand _CancelCommand; 8  

Eso es todo.
Espero que esto ayude.

 

Yes, since you are here for code review:

  1. Never ever ignore errors: yr, _ := strconv.Atoi(f[0])
        yr, err := strconv.Atoi(f[0])         if err != nil {             log.Fatal(err)         } 
  1. You may use just one strings.Split/strings.Fields this way:
    f := strings.Fields(logData)     for i := 3; i <= len(f); i += 3 {         yr, err := strconv.Atoi(f[i-3])         if err != nil {             log.Fatal(err)         }         amt, err := strconv.Atoi(f[i-1])         if err != nil {             log.Fatal(err)         }         r := logRec{yr, f[i-2], amt}         p.Report(r)     } 
  1. You are over complicating a simple sum function, You don't need channels here, just a map does the job: It is this simple:
package main  import (     "fmt"     "log"     "strconv"     "strings" )  func main() {     m := map[string]int{}     f := strings.Fields(logData)     for i := 3; i <= len(f); i += 3 {         amt, err := strconv.Atoi(f[i-1])         if err != nil {             log.Fatal(err)         }         m[f[i-2]] += amt     }     fmt.Println(m) }  const logData = `2018 apples 9 2018 oranges 5 2019 apples 27 2019 lemons 13 2019 oranges 2 2020 oranges 1 2020 apples 16 2020 lemons 3`  
  1. For concurrency sake: You only need concurrent map, nothing more:
package main  import (     "fmt"     "log"     "strconv"     "strings"     "sync" )  type concurrentMap struct {     sync.Mutex     m map[string]int }  func (p *concurrentMap) sum(item string, amount int) {     p.Lock()     p.m[item] += amount     p.Unlock() }  var data = &concurrentMap{m: map[string]int{}}  func main() {     f := strings.Fields(logData)     for i := 3; i <= len(f); i += 3 {         amt, err := strconv.Atoi(f[i-1])         if err != nil {             log.Fatal(err)         }         data.sum(f[i-2], amt)     }     fmt.Println(data.m) }  const logData = `2018 apples 9 2018 oranges 5 2019 apples 27 2019 lemons 13 2019 oranges 2 2020 oranges 1 2020 apples 16 2020 lemons 3` 
  1. Long running task example:
package main  import (     "fmt"     "log"     "strconv"     "strings"     "sync"     "time" )  type concurrentMap struct {     m map[string]int     sync.Mutex }  func (p *concurrentMap) sum(item string, amount int) {     p.Lock()     p.m[item] += amount     p.Unlock() } func (p *concurrentMap) show() {     p.Lock()     for k, v := range p.m {         fmt.Printf("Total for %s is %d\n", k, v)     }     p.Unlock() }  var data = &concurrentMap{m: map[string]int{}}  func main() {     go data.sum("oranges", 100)     go data.sum("apples", 100)     go data.sum("lemons", 100)      go func() {         f := strings.Fields(logData)         for i := 3; i <= len(f); i += 3 {             amt, err := strconv.Atoi(f[i-1])             if err != nil {                 log.Fatal(err)             }             data.sum(f[i-2], amt)             time.Sleep(1000 * time.Millisecond) // e.g. slow hard disk         }     }()      t := time.NewTicker(1000 * time.Millisecond)     defer t.Stop()     for range t.C {         data.show()         fmt.Println()     } }  const logData = `2018 apples 9 2018 oranges 5 2019 apples 27 2019 lemons 13 2019 oranges 2 2020 oranges 1 2020 apples 16 2020 lemons 3` 

And finally if your tasks will finish in time, you may use sync.WaitGroup:

package main  import (     "fmt"     "log"     "strconv"     "strings"     "sync"     "time" )  type concurrentMap struct {     m map[string]int     sync.Mutex }  func (p *concurrentMap) sum(item string, amount int) {     p.Lock()     p.m[item] += amount     p.Unlock() } func (p *concurrentMap) show() {     p.Lock()     for k, v := range p.m {         fmt.Printf("Total for %s is %d\n", k, v)     }     p.Unlock() }  var data = &concurrentMap{m: map[string]int{}}  func main() {     finished := &sync.WaitGroup{}     finished.Add(1)     go func() {         defer finished.Done()         data.sum("oranges", 100)         data.sum("apples", 100)         data.sum("lemons", 100)     }()      finished.Add(1)     go func() {         defer finished.Done()         f := strings.Fields(logData)         for i := 3; i <= len(f); i += 3 {             amt, err := strconv.Atoi(f[i-1])             if err != nil {                 log.Fatal(err)             }             data.sum(f[i-2], amt)             time.Sleep(100 * time.Millisecond) // e.g. slow hard disk         }     }()      finished.Wait()     data.show() }  const logData = `2018 apples 9 2018 oranges 5 2019 apples 27 2019 lemons 13 2019 oranges 2 2020 oranges 1 2020 apples 16 2020 lemons 3` 

That is all.
I hope this helps.

 
 
 
 

Relacionados problema

2  Método de actualización de bloqueo de reentrantreadwritelock  ( Reentrantreadwritelock lock upgrade method ) 
Tengo una pregunta sobre la actualización de bloqueo. Específicamente lo que me molesta está entre readlock.unlock () y siguiendo a writelock.lock () ... Esto...

4  Cola de tareas paralelas que se ejecuta en secuencia  ( Parallel task queue that runs in sequence ) 
Necesito una solución que ejecute solicitudes entrantes constantemente en una secuencia por recurso, pero paralela en general. El caso de uso: Muchos clie...

7  Descarga concurrente en Go  ( Concurrent download in go ) 
Escribí un programa para descargar archivos en manera concurrente / paralelo (gomaxprocs & gt; 1). Este es mi programa 2 nd (no juguete) escrito en Go. Po...

3  SPSC espera un tampón de anillo libre para mensajes entrantes  ( Spsc wait free ring buffer for incoming messages ) 
Esto es para un solo productor y un solo tampón de anillo de espera libre de consumidores. Las escrituras necesitan ser esperadas por supuesto. Pre-asigna las...

3  Implementando una fábrica segura de hilo con almacenamiento en caché  ( Implementing a thread safe factory with caching ) 
Tengo un 9988776655544330 que crea Connector Objetos basados ​​en parámetros como URL, nombre de usuario y contraseñas. El Connector2 El objeto implem...

4  Pruebe una actualización especulativa, concurrente, libre de bloqueo y atómica hasta que las coincidencias de la condición de aburtar  ( Try a speculative concurrent lock free atomic update until abort condition ma ) 
Por favor, hágamelo saber si ve las mejoras de rendimiento, errores o cualquier cosa que cambie y por qué. public static bool TrySpeculativeUpdate(ref ...

6  Al leer simultáneamente un mapa, mientras que un solo hilo de fondo lo modifica regularmente  ( Concurrently reading a map while a single background thread regularly modifies i ) 
Tengo una clase en la que estoy poblando un mapa liveSocketsByDatacenter desde una sola hilo de fondo cada 30 segundos dentro 9988777665544334 y luego ten...

12  Entrevista de concurrencia  ( Concurrency interview ) 
Un poco de vuelta, tuve una entrevista donde plantearon un problema, en resumen: Mire un determinado directorio, y procese archivos JSON entrantes Estos ...

5  Herramienta para calcular el tiempo promedio que toma para una solicitud de tracción GitHub para fusionar  ( Tool to calculate the average time that takes for a github pull request to get m ) 
Estoy aprendiendo, y este es mi primer intento de una herramienta de línea de comandos que usa la API de GitHub para calcular el tiempo promedio que toma una ...

3  Diseño de reutilización de la piscina de hilo con Ejecutors.NewFixedThreadPool en Java  ( Design of thread pool reuse with executors newfixedthreadpool in java ) 
En mi solicitud, tengo código que debería ejecutarse cada hora en un nuevo elemento y ejecutar una operación costosa con MultiPhreading: |2 Mis pregunt...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos