Waiting for goroutines to finish
Yes, since you are here for code review:

Never ever ignore errors: yr, _ := strconv.Atoi(f[0])

        yr, err := strconv.Atoi(f[0])
        if err != nil {
            log.Fatal(err)
        }


You may use just one strings.Split/strings.Fields this way:

    f := strings.Fields(logData)
    for i := 3; i &lt;= 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)
    }


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 (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;strconv&quot;
    &quot;strings&quot;
)

func main() {
    m := map[string]int{}
    f := strings.Fields(logData)
    for i := 3; i &lt;= 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`



For concurrency sake: You only need concurrent map, nothing more:

package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;strconv&quot;
    &quot;strings&quot;
    &quot;sync&quot;
)

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 = &amp;concurrentMap{m: map[string]int{}}

func main() {
    f := strings.Fields(logData)
    for i := 3; i &lt;= 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`


Long running task example:

package main

import (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;strconv&quot;
    &quot;strings&quot;
    &quot;sync&quot;
    &quot;time&quot;
)

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(&quot;Total for %s is %d\n&quot;, k, v)
    }
    p.Unlock()
}

var data = &amp;concurrentMap{m: map[string]int{}}

func main() {
    go data.sum(&quot;oranges&quot;, 100)
    go data.sum(&quot;apples&quot;, 100)
    go data.sum(&quot;lemons&quot;, 100)

    go func() {
        f := strings.Fields(logData)
        for i := 3; i &lt;= 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 (
    &quot;fmt&quot;
    &quot;log&quot;
    &quot;strconv&quot;
    &quot;strings&quot;
    &quot;sync&quot;
    &quot;time&quot;
)

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(&quot;Total for %s is %d\n&quot;, k, v)
    }
    p.Unlock()
}

var data = &amp;concurrentMap{m: map[string]int{}}

func main() {
    finished := &amp;sync.WaitGroup{}
    finished.Add(1)
    go func() {
        defer finished.Done()
        data.sum(&quot;oranges&quot;, 100)
        data.sum(&quot;apples&quot;, 100)
        data.sum(&quot;lemons&quot;, 100)
    }()

    finished.Add(1)
    go func() {
        defer finished.Done()
        f := strings.Fields(logData)
        for i := 3; i &lt;= 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.