// 3. Teil des Go-Tutorials // Listing-Auszuege aus dem Heft // Der komplette Beispielcode // des GitHub Progress Monitor ist unter // https://github.com/themue/ghpm/ // zu finden. Listing 1: Start von Goroutinen // Start a simple function. go CopyFile("foo.txt", "bar.txt") // Start a method. go myAnalyzer.Process(input, output) // Start a local function. go func() { in := readFile("in.txt") out := process(in) writeFile("out.txt", out) }() Listing 2: Ergebnisrückgabe // Start finding answer in background. answerChan := make(chan Answer) go findAnswerToAllQuestions(answerChan) // Do something else. ... // Read answer. answer := <-answerChan Listing 3: Timeout select { case answer := <-answerChan: // Process answer. ... case <-time.After(5*time.Second): // Handle timeout. ... } Listing 4: Aneinanderfügen von Strings package appender type Appender struct { content string AppendChan chan string ContentChan chan chan string ResetChan chan string } func New(content string) *Appender { a := &Appender{ content: content, AppendChan: make(chan string), ContentChan: make(chan chan string), ResetChan: make(chan string), } go a.backend() return a } Listing 5: Channel-Abfrage func (a *Appender) backend() { for { select { case content := <-a.AppendChan: a.content = append(a.content, content) case contentChan := <-a.ContentChan: contentChan <- a.content case content := <-a.ResetChan: a.content = content } } } Listing 6: Zusammenspiel der Goroutinen // Goroutine A. a := appender.New("foo") // Goroutine B. a.AppendChan <- "bar" a.AppendChan <- "baz" // Goroutine C. a.AppendChan <- "yadda" // Goroutine A. resultChan := make(chan string) a.ContentChan <- resultChan result := <-resultChan a.ResetChan <- "" Listing 7: Übertragung von Funktionen type Appender struct { ctx context.Context content string messageChan chan func() } func New(ctx context.Context, content string) *Appender { a := &Appender{ ctx: ctx, content: content, messageChan: make(chan func()), } go a.backend() return a } func (a *Appender) backend() { for { select { case <-a.ctx.Done(): a.content = "" return case method := <-a.messageChan: method() } } } Listing 8: Implentierung der Funktion func (a *Appender) Append(content string) { a.messageChan <- func() { a.content = append(a.content, content) } } func (a *Appender) Content() string { var content string a.messageChan <- func() { content = a.content } return content } Listing 9: Aufgaben bündeln type Job struct { ID string Owner string Repo string GitHubOptions []github.Option Interval time.Duration EventsAnalyzers []analyze.EventsAnalyzer Accumulate Accumulator } Listing 10: Abarbeitung des Job type Poller struct { ctx context.Context job *Job eventor *github.RepoEventor collector *Collector } Listing 11: Abfrage mit time.Ticker func SpawnPoller(ctx context.Context, job *Job, c *Collector) { p := &Poller{ ctx: ctx, job: job, eventor: github.NewRepoEventor(job.Owner, job.Repo, job.GitHubOptions...), collector: c, } go p.backend() } func (p *Poller) backend() { ticker := time.NewTicker(p.job.Interval) defer ticker.Stop() for { select { case <-p.ctx.Done(): return case <-ticker.C: p.collector.HandleResult(p.analyze()) } } } Listing 12: Ergebnis-Channel func NewCollector(ctx context.Context) *Collector { c := &Collector{ ctx: ctx, messageChan: make(chan func()) accumulates: make(map[string]analyze.Accumulation), } go c.backend() return c } Listing 13: Ergebnisrückgabe func (c *Collector) HandleResult(result *Result) { c.messageChan <- func() { if r.Err != nil { log.Printf("error in poll job %q: %v", result.Job.ID, result.Err) return } acc, ok := c.accumulates[result.Job.ID] if !ok { // Job ID not yet known. acc = analyze.Accumulation{} } log.Printf("accumulating job %q ...", result.Job.ID) c.accumulates[result.Job.ID] = result.Job.Accumulate(acc, result.Accumulation) } } Listing 14: Backend des Collector func (c *Collector) backend() { defer close(c.messageChan) for { select { case <-c.ctx.Done(): return case method := <-c.messageChan: method() } } } Listing 15: Poller im Hintergrund func SpawnPollers(ctx context.Context, jobs Jobs, collector *Collector) { for _, job := range jobs { SpawnPoller(ctx, job, collector) } }