2013-03-30 2 views
8

Ich versuche, einige Berechnungen parallel zu machen. Das Programm ist so ausgelegt, dass jede Worker-Goroutine "Teile" eines gelösten Puzzles an die Controller-Routine zurücksendet, die darauf wartet, alles zu empfangen und zu assemblieren, das von den Worker-Routinen gesendet wurde.Wie schließe ich einen Kanal, auf dem mehrere Goroutines senden?

Was ist die Idomatik zum Schließen des Einzelkanals? Ich kann den Kanal nicht in jeder Goroutine aufrufen, weil ich dann möglicherweise einen geschlossenen Kanal senden könnte. Ebenso gibt es keine Möglichkeit vorher festzulegen, welche Goroutine als erstes beendet wird. Ist hier eine sync.WaitGroup notwendig?

Antwort

9

Hier ist ein Beispiel der sync.WaitGroup mit tun, was Sie suchen,

Dieses Beispiel eine lenghty Liste von ganzen Zahlen akzeptiert, fasst sie dann alle nach oben durch N Gabe parallel Arbeiter einen gleich großen Brocken des Eingangs Daten. Es kann auf go playground betrieben werden:

package main 

import (
    "fmt" 
    "sync" 
) 

const WorkerCount = 10 

func main() { 
    // Some input data to operate on. 
    // Each worker gets an equal share to work on. 
    data := make([]int, WorkerCount*10) 

    for i := range data { 
     data[i] = i 
    } 

    // Sum all the entries. 
    result := sum(data) 

    fmt.Printf("Sum: %d\n", result) 
} 

// sum adds up the numbers in the given list, by having the operation delegated 
// to workers operating in parallel on sub-slices of the input data. 
func sum(data []int) int { 
    var sum int 

    result := make(chan int) 
    defer close(result) 

    // Accumulate results from workers. 
    go func() { 
     for { 
      select { 
      case value := <-result: 
       sum += value 
      } 
     } 
    }() 

    // The WaitGroup will track completion of all our workers. 
    wg := new(sync.WaitGroup) 
    wg.Add(WorkerCount) 

    // Divide the work up over the number of workers. 
    chunkSize := len(data)/WorkerCount 

    // Spawn workers. 
    for i := 0; i < WorkerCount; i++ { 
     go func(i int) { 
      offset := i * chunkSize 

      worker(result, data[offset:offset+chunkSize]) 
      wg.Done() 
     }(i) 
    } 

    // Wait for all workers to finish, before returning the result. 
    wg.Wait() 

    return sum 
} 

// worker sums up the numbers in the given list. 
func worker(result chan int, data []int) { 
    var sum int 

    for _, v := range data { 
     sum += v 
    } 

    result <- sum 
} 
+0

Vielen Dank, das hat perfekt funktioniert für das, was ich brauchte. –

+0

Ein Teil dieses Codes ist ein wenig ... seltsam. Insbesondere die Goroutine mit dem for/single-case-select akkumuliert die Ergebnisse und überschreibt eine Variable ohne Synchronisation. Einige kleinere Umordnungen und Dinge werden zuverlässiger/einfacher zu verstehen: http://play.golang.org/p/5bmlTbdIQa – Dustin

4

Ja, dies ist ein perfekter Anwendungsfall für sync.WaitGroup.

Ihre andere Option ist die Verwendung von 1 Kanal pro Goroutine und einer Multiplexer-Routine, die von jedem Kanal in einen einzelnen Kanal einspeist. Aber das würde schnell unhandlich werden, also würde ich einfach mit einer sync.WaitGroup gehen.

Verwandte Themen