4. ❝Parallelism
Programming as the simultaneous
execution of (possibly related)
computations.
Concurrency
Programming as the composition
of independently executing
processes.
Rob Pike
31. Google Go als Multi-Paradigmen-Sprache
imperativ
bedingt funktional
bedingt objektorientiert
nebenläufig
32. ❝It’s better to have a
permanent income
than to be
fascinating.
Oscar Wilde
33. Nebenläufigkeit in Go
• Leichtgewichtige Goroutines im Thread
Pool
• Sehr große Anzahl gleichzeitig möglich
• Kommunikation und Synchronisation über
Channels
• Vielfältige Kontrolle durch select
Statement
34. Kapselung in Typen
package service
type Service struct {
thisChan chan *This
thatChan chan *That
foo bool
bar int
baz string
}
35. Konstruktoren sind Funktionen
func NewService(...) *Service {
s := &Service{
thisChan: make(chan *This),
thatChan: make(chan *That, 10),
…,
}
// Start of the backend loop as goroutine.
go s.loop()
return s
}
36. Endlosschleifen und select
func (s *Service) loop() {
ticker := time.NewTicker(time.Second)
for {
select {
case this := <-s.thisChan:
s.doThis(this)
case that := <-s.thatChan:
s.doThat(that)
case <-ticker.C:
s.doTick()
}
}
}
37. Methoden als externe Schnittstellen
func (s *Service) DoThis(data string) int {
respChan := make(chan int)
this := &This{data, respChan}
s.thisChan <- this
return <-respChan
}
49. Sehr naives Parallelisieren (1)
func process(in []int) []int {
resultChan := make(chan int)
for _, value := range in {
// One goroutine per value.
go processValue(value, resultChan)
}
// Collecting the results.
out := make([]int, len(in))
for i := 0; i < len(in); i++ {
out[i] = <-resultChan
}
return out
}
50. Sehr naives Parallelisieren (2)
func processValue(
inValue int, resultChan chan int) {
// Time of result calculation may vary.
outValue := inValue …
resultChan <- outValue
}
51. Problem und Verbesserung
• Unterschiedliche Laufzeiten führen zu
falscher Indizierung im Ergebnis
• Index in der Verarbeitung mitführen
• Ergebnis gemäß Index setzen
52. Verbesserung (1)
func process(in []int) []int {
resultChan := make(chan *result)
for index, value := range in {
// One goroutine per value.
go processValue(index, value, resultChan)
}
out := make([]int, len(in))
for i := 0; i < len(in); i++ {
result <- resultChan
out[result.index] = result.value
}
return out
}
53. Verbesserung (2)
type result struct {
index int
value int
}
func processValue(
index, inValue int, resultChan chan *result) {
// Time of result calculation may vary.
outValue := inValue …
// Send index with result.
resultChan <- &result{index, outValue}
}
54. Isolierung von Zugriffen
• Loops serialisieren Zugriffe
• Synchronisation über Channels oder
Package sync
• Referenzen nur an eine Goroutine senden
• Aufteilung von Arrays und Slices
55. Arbeitsteilung (1)
// Process all data passed by reference.
func process(ds []*Data) {
var wg sync.WaitGroup
for i := 0; i < len(ds); i += chunkSize {
// Process one chunk in the background.
go processChunk(ds[i:i+chunkSize], wg)
}
// Wait for all processing to complete.
wg.Wait()
}
56. Arbeitsteilung (2)
// Processing a chunk of data passed by reference.
func processChunk(ds []*Data, wg sync.WaitGroup) {
// Increment WaitGroup counter and signal when done.
wg.Add(1)
defer wg.Done()
// Process each data.
for _, d := range ds {
if d != nil {
// Direct manipulation.
d.Foo = ...
...
}
}
}
57. Kontrolle von Goroutines
• Keine direkte Beziehung zwischen
Goroutines
• Kontrolle nur über Channels
• Externe Packages helfen
• gopkg.in/tomb.v2
• github.com/tideland/goas/v2/loop
58. Operation in einer Schleife
func (t *T) loop() {
defer t.tomb.Done()
for {
select {
case <-t.tomb.Dying():
// Cleanup ...
return
case f := <-t.fooChan:
if err := t.foo(f); err != nil {
t.tomb.Kill(err)
}
case b := <-t.barChan:
// ...
}
}
}
59. Steuerung von außen
// Stop ends the main loop.
func (t *T) Stop() error {
// Kill(nil) means no error as reason.
t.tomb.Kill(nil)
return t.tomb.Wait()
}
// Err retrieves the error in case the backend loop died.
func (t *T) Err() error {
return t.tomb.Err()
}
60. Prüfung des Backends
func (t *T) Foo(foo *Foo) (*Bar, error) {
env := &fooEnv{foo, make(chan *Bar)}
select {
case t.fooChan <- env:
case <-t.tomb.Dead():
return nil, errors.New("backend dead")
}
select {
case bar := <- env.barChan:
return bar, nil
case <-t.tomb.Dead():
return nil, errors.New("backend dead")
}
}
67. Hilfreiche Packages
• net, …/http für IP und Web
• html/template, mime/… für Inhalte
• encoding/gob, …/json, …/xml für die
Serialisierung
• compress/… zur Komprimierung
• crypto/… zur Verschlüsselung
68. Externe Packages von Google
• WebSocket, SPDY, Dict und mehr in
golang.org/x/net
• Weitere Crypto Packages (u.a. OpenPGP)
in golang.org/x/crypto
69. Serialisierung via JSON
type Parameters map[string]interface{}
type Action struct {
Tag string `json:"tag"`
Receiver string `json:"receiver"`
Name string `json:"name"`
Parameters Parameters `json:"parameters,omitempty"`
}