Add German translation of chapter 2

This commit is contained in:
digitalcraftsman
2015-08-07 22:53:30 +02:00
parent c60c38103e
commit 4da3f25821
9 changed files with 1429 additions and 1412 deletions

View File

@@ -1,242 +1,244 @@
# Concurrency
# 2.7 Nebenläufigkeit
It is said that Go is the C language of the 21st century. I think there are two reasons: first, Go is a simple language; second, concurrency is a hot topic in today's world, and Go supports this feature at the language level.
Es wird behauptet, Go sei das C des 21. Jahrhunderts. Ich glaube, dafür gibt es zwei Gründe: erstens, Go ist eine simple Programmiersprache; zweitens: Nebenläufigkeit (Concurrency im Englischen) ist ein heißes Thema in der heutigen Welt und Go unterstützt die Eigenschaft als ein zentraler Aspekt der Sprache.
## goroutine
## Goroutinen
goroutines and concurrency are built into the core design of Go. They're similar to threads but work differently. More than a dozen goroutines maybe only have 5 or 6 underlying threads. Go also gives you full support to sharing memory in your goroutines. One goroutine usually uses 4~5 KB of stack memory. Therefore, it's not hard to run thousands of goroutines on a single computer. A goroutine is more lightweight, more efficient and more convenient than system threads.
Goroutinen und Nebenläufigkeit sind zwei wichtige Komponenten im Design von Go. Sie ähneln Threads, funktionieren aber auf eine andere Weise. Ein dutzend Goroutinen haben vielleicht nur fünf oder sechs zugrundeliegende Threads. Des Weiteren unterstützt Go vollständig das Teilen von Speicherressourcen zwischen den Goroutinen. Eine Goroutine nimmt gewöhnlicherweise etwa 4 bis 5 KB Speicher ein. Daher ist es nicht schwer, tausende von Goroutinen auf einem einzelnen Computer zu nutzen. Goroutinen sind weniger ressourcenhungrig, effizienter und geeigneter als Systemthreads.
goroutines run on the thread manager at runtime in Go. We use the `go` keyword to create a new goroutine, which is a function at the underlying level ( ***main() is a goroutine*** ).
Goroutinen laufen im Thread Manager während der Laufzeit von Go. Wir nutzen das Schlüsselwort `go`, um eine neue Goroutine zu erstellen, wobei es sich eigentlich um eine interne Funktion von Go handelt ( ***main() ist ebenfalls eine Goroutine*** ).
go hello(a, b, c)
Let's see an example.
go Hallo(a, b, c)
Schauen wir uns ein Beispiel an.
package main
package main
import (
"fmt"
"runtime"
)
import (
"fmt"
"runtime"
)
func say(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func sag(s string) {
for i := 0; i < 5; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
go say("world") // create a new goroutine
say("hello") // current goroutine
}
func main() {
go sag("Welt") // Erzeugt eine neue Goroutine
sag("Hallo") // Aktuelle Goroutine
}
Output
Ausgabe:
hello
world
hello
world
hello
world
hello
world
hello
We see that it's very easy to use concurrency in Go by using the keyword `go`. In the above example, these two goroutines share some memory, but we would better off following the design recipe: Don't use shared data to communicate, use communication to share data.
Hallo
Welt
Hallo
Welt
Hallo
Welt
Hallo
Welt
Hallo
Wie es scheint, ist es sehr einfach, Nebenläufigkeit in Go durch das Schlüsselwort `go` zu nutzen. Im oberen Beispiel teilen sich beide Goroutinen den selben Speicher. Aber es wäre besser, diesem Rat folge zu leisten: Nutze keine geteilten Daten zur Kommunikation, sondern kommuniziere die geteilten Daten.
runtime.Gosched() means let the CPU execute other goroutines, and come back at some point.
`runtime.Gosched()` bedeutet, das die CPU andere Goroutinen ausführen und nach einiger Zeit an den Ausgangspunkt zurückkehren soll.
The scheduler only uses one thread to run all goroutines, which means it only implements concurrency. If you want to use more CPU cores in order to take advantage of parallel processing, you have to call runtime.GOMAXPROCS(n) to set the number of cores you want to use. If `n<1`, it changes nothing. This function may be removed in the future, see more details about parallel processing and concurrency in this [article](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
Das Steuerungsprogramm nutzt einen Thread, um alle Goroutinen auszuführen. Das bedeutet, dass einzig dort Nebenläufigkeit implementiert wird. Möchtest Du mehr Rechnenkerne im Prozessor nutzen, um die Vorteile paralleler Berechnungen einzubringen, musst Du `runtime.GOMAXPROCS(n)` aufrufen, um die Anzahl der Rechenkerne festzulegen. Gilt `n<1`, verändert sich nichts. Es könnte sein, dass diese Funktion in Zukunft entfernt wird. Für weitere Informationen zum verteilten Rechnen und Nebenläufigkeit findest Du in diesem [Artikel](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
## channels
## Channels
goroutines run in the same memory address space, so you have to maintain synchronization when you want to access shared memory. How do you communicate between different goroutines? Go uses a very good communication mechanism called `channel`. `channel` is like a two-way pipeline in Unix shells: use `channel` to send or receive data. The only data type that can be used in channels is the type `channel` and the keyword `chan`. Be aware that you have to use `make` to create a new `channel`.
Goroutinen werden im selben Adressraum des Arbeitsspeichers ausgeführt, sodass Du in den Goroutinen die genutzen Ressourcen synchronisieren musst, wenn diese geteilt werden sollen. Aber wie kommuniziere ich zwischen verschiedenen Goroutinen? Hierfür nutzt Go einen sehr guten Mechanismus mit dem Namen `channel`. `channel` ist wie eine bidirektionale Übertragungsleitung (Pipe) in Unix-Shells: nutze `channel` um Daten zu senden und zu empfangen. Der einzige Datentyp, der in Kombination mit diesen Datenkanälen genutzt werden kann, ist der Typ `channel` und das Schlüsselwort `chan`. Beachte, dass Du `make` brauchst, um einen neuen `channel` zu erstellen.
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
channel uses the operator `<-` to send or receive data.
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
`channel` nutzt den Operator `<-`, um Daten zu senden und zu empfangen.
ch <- v // send v to channel ch.
v := <-ch // receive data from ch, and assign to v
Let's see more examples.
ch <- v // Sende v an den Kanal ch.
v := <-ch // Empfange Daten von ch und weise sie v zu
Schauen wir uns weitere Beispiele an.
package main
package main
import "fmt"
import "fmt"
func sum(a []int, c chan int) {
total := 0
for _, v := range a {
total += v
}
c <- total // send total to c
}
func summe(a []int, c chan int) {
gesamt := 0
for _, v := range a {
gesamt += v
}
c <- gesamt // Sende gesamt an c
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c // receive from c
c := make(chan int)
go summe(a[:len(a)/2], c)
go summe(a[len(a)/2:], c)
x, y := <-c, <-c // Empfange Daten von c
fmt.Println(x, y, x + y)
}
Sending and receiving data in channels blocks by default, so it's much easier to use synchronous goroutines. What I mean by block is that a goroutine will not continue when receiving data from an empty channel, i.e (`value := <-ch`), until other goroutines send data to this channel. On the other hand, the goroutine will not continue until the data it sends to a channel, i.e (`ch<-5`), is received.
fmt.Println(x, y, x + y)
}
## Buffered channels
Das Senden und Empfangen von Daten durch die Datenkanäle wird standardmäßig gestoppt, um die Goroutinen einfach synchron zu halten. Mit dem Blocken meine ich, dass eine Goroutine nicht weiter ausgeführt wird, sobald keine Daten mehr von einem `channel` empfangen werden (z.B. `value := <-ch`) und andere Goroutinen keine weiteren Daten über den entsprechenden Kanal senden. Anderseits stoppt die sendende Goroutine solange, bis alle Daten (z.B. `ch<-5`)über den Kanal empfangen wurden.
I introduced non-buffered channels above. Go also has buffered channels that can store more than a single element. For example, `ch := make(chan bool, 4)`, here we create a channel that can store 4 boolean elements. So in this channel, we are able to send 4 elements into it without blocking, but the goroutine will be blocked when you try to send a fifth element and no goroutine receives it.
## Gepufferte Channels
ch := make(chan type, n)
Eben habe ich die nicht-gepuffter Datenkanäle vorgestellt. Go unterstützt aber auch gepufferte Channel, die mehr als ein Element speichern können, z.B. `ch := make(chan bool, 4)`. Hier wurde ein Channel mit der Kapazität von vier Booleans erstellt. Mit diesem Datenkanal sind wir in der Lage, vier Elemente zu senden, ohne das die Goroutine stoppt. Dies passiert aber bei dem Versuch, ein fünftes Element zu versenden, ohne das es von einer Goroutine empfangen wird.
n == 0 ! non-bufferblock
n > 0 ! buffernon-block until n elements in the channel
You can try the following code on your computer and change some values.
package main
ch := make(chan type, n)
import "fmt"
n == 0 ! nicht-gepuffertstoppt
n > 0 ! gepuffertnicht gestoppt, sobald n Elemente im Kanal sind
Experimentiere mit dem folgenden Code auf Deinem Computer und verändere die Werte.
package main
func main() {
c := make(chan int, 2) // change 2 to 1 will have runtime error, but 3 is fine
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
## Range and Close
import "fmt"
We can use range to operate on buffer channels as in slice and map.
func main() {
c := make(chan int, 2) // Setze 2 auf 1 und Du erzeugst einen Laufzeitfehler. Aber 3 ist OK.
c <- 1
c <- 2
fmt.Println(<-c)
fmt.Println(<-c)
}
## Range und Close
package main
Wir können `range` in gepufferten Kanlen genauso nutzen, wie mit Slices und Maps.
import (
"fmt"
)
package main
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x + y
}
close(c)
}
import (
"fmt"
)
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
`for i := range c` will not stop reading data from channel until the channel is closed. We use the keyword `close` to close the channel in above example. It's impossible to send or receive data on a closed channel; you can use `v, ok := <-ch` to test if a channel is closed. If `ok` returns false, it means the there is no data in that channel and it was closed.
func fibonacci(n int, c chan int) {
x, y := 1, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x + y
}
close(c)
}
Remember to always close channels in producers and not in consumers, or it's very easy to get into panic status.
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
`for i := range c` wird nicht eher mit dem Lesen von Daten aus dem Channel aufhören, ehe dieser geschlossen ist. Wir nutzen das Schlüsselwort `close`, um den Datenkanal im oberen Beispiel zu schließen. Es ist unmöglich, Daten über einen geschlossenen Channel zu senden oder zu empfangen. Mit `v, ok := <-ch` kannst Du den Status eines Kanals überprüfen. Wird `ok` auf false gesetzt, bedeutet dies, dass sich keine weiteren Daten im Channel befinden und er geschlossen wurde.
Another thing you need to remember is that channels are not like files. You don't have to close them frequently unless you are sure the channel is completely useless, or you want to exit range loops.
Denke aber immer daran, die Datenkanäle auf seiten der Datenproduzenten zu schließen und nicht bei den Empfängern der Daten. Andernfalls kann es passieren, dass sich Dein Programm in den Panikmodus versetzt.
Ein weiterer Aspekt, den wir nicht unterschlagen sollten, ist, dass Du Channels nicht wie Dateien behandeln solltest. Du brauchst sie nicht andauernd schließen, sondern erst, wenn Du sicher bist, dass sie nicht mehr gebraucht werden oder Du das Abfragen der übertragenen Daten mit dem Schlüsselwort `range` beenden willst.
## Select
In the above examples, we only use one channel, but how can we deal with more than one channel? Go has a keyword called `select` to listen to many channels.
In den vorherigen Beispielen haben wir bisher immer nur einen Datenkanal verwendet, aber wie können wir Gebrauch von mehreren Channels machen? Go erlaubt es, mit dem Schlüsselwort `select` viele Kanäle nach Daten zu belauschen.
`select` is blocking by default and it continues to execute only when one of channels has data to send or receive. If several channels are ready to use at the same time, select chooses which to execute randomly.
`select` stoppt standardmäßig eine Goroutine und wird einzig ausgeführt, wenn einer der Channels Daten sendet oder empfängt. Sollten mehrere Kanäle zur gleichen Zeit aktiv sein, wird ein zufälliger ausgewählt.
package main
package main
import "fmt"
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x, y = y, x + y
case <-quit:
fmt.Println("quit")
return
}
}
}
func fibonacci(c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x:
x, y = y, x + y
case <-quit:
fmt.Println("Fertig")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
`select` has a `default` case as well, just like `switch`. When all the channels are not ready for use, it executes the default case (it doesn't wait for the channel anymore).
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
select {
case i := <-c:
// use i
default:
// executes here when c is blocked
}
## Timeout
`select` hat ebenfalls einen Standardfall wie `switch`, mit dem Namen `default`. Wenn kein Datenkanal aktiv sein sollte, wird der Standardfall ausgeführt (es wird auf keinen Kanal mehr gewartet).
Sometimes a goroutine becomes blocked. How can we avoid this to prevent the whole program from blocking? It's simple, we can set a timeout in the select.
select {
case i := <-c:
// Benutze i
default:
// Dieser Code wird ausgeführt, sollte c gestoppt worden sein
}
## Zeitüberschreitung
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
Manchmal kann es vorkommen, dass eine Goroutine gestoppt wird. Wie können wir verhindern, dass daraus resultierend das gesamte Programm aufhört zu arbeiten? Es ist ganz einfach. Es muss lediglich eine Zeitüberschreitung in `select` festgelegt werden.
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("Zeitüberschreitung")
o <- true
break
}
}
}()
<- o
}
## Runtime goroutine
The package `runtime` has some functions for dealing with goroutines.
Das Paket `runtime` beinhaltet ein paar Funktionen zum Umgang mit Goroutinen.
- `runtime.Goexit()`
Exits the current goroutine, but defered functions will be executed as usual.
Verlässt die aktuelle Goroutine, aber verzögerte Funktionen werden wie gewohnt ausgeführt.
- `runtime.Gosched()`
Lets the scheduler execute other goroutines and comes back at some point.
Lässt die CPU vorerst andere Goroutinen ausführen und kehrt nach einiger Zeit zum Ausgangspunkt zurück.
- `runtime.NumCPU() int`
Returns the number of CPU cores
Gibt die Anzahl der Rechenkerne zurück.
- `runtime.NumGoroutine() int`
Returns the number of goroutines
Gibt die Anzahl der Goroutinen zurück.
- `runtime.GOMAXPROCS(n int) int`
Sets how many CPU cores you want to use
Legt die Anzahl der Rechenkerne fest, die benutzt werden sollen.
## Links
- [Directory](preface.md)
- Previous section: [interface](02.6.md)
- Next section: [Summary](02.8.md)
- [Inhaltsverzeichnis](preface.md)
- Vorheriger Abschnitt: [Interfaces](02.6.md)
- Nächster Abschnitt: [Zusammenfassung](02.8.md)