Структура счетчик в конкурентной среде

Всем привет. Стоит задача разработать структуру счетчик который работает в конкурентной среде. Не уверен, что правильно понял термин "структура-счетчик". Может кто-то знает, что значит структура счетчик? Я понял так

package main

import (
    "sync"

    "github.com/udonetsm/help/helper"
)

type Counter struct {
    num int
    sync.RWMutex
}

func (c *Counter) Count(wg *sync.WaitGroup, finish, start int) int {
    defer helper.PanicCapture("Count")
    wg.Add(finish - start)
    for c.num < finish {
        go func() {
            c.Lock()
            c.num += 1
            c.Unlock()
            wg.Done()
        }()
    }
    return c.num
}

func New(start int) *Counter {
    defer helper.PanicCapture("INIT")
    return &Counter{
        num: start,
    }
}

func Counting(start, finish int) {
    defer helper.PanicCapture("Counting")
    c := New(start)
    wg := sync.WaitGroup{}
    c.Count(&wg, finish, start)
    wg.Wait()
}

Постоянно выдает панику panic: sync: negative WaitGroup counter

package main

import (
    "log"
    "sync"
    "time"

    "github.com/udonetsm/help/helper"
)

type Counter struct {
    num int
    sync.RWMutex
}

func (c *Counter) Count(wg *sync.WaitGroup, finish, start int) int {
    defer helper.PanicCapture("Count")
    wg.Add(1)
    go func() {
        for i := start; i <= finish; i++ {
            c.Lock()
            c.num += 1
            c.Unlock()
            log.Println(c.num)
            time.Sleep(time.Second)
        }
        wg.Done()
    }()
    wg.Wait()
    return c.num
}

func New(start int) *Counter {
    defer helper.PanicCapture("INIT")
    return &Counter{
        num: -1,
    }
}

func Counting(start, finish int) {
    defer helper.PanicCapture("Counting")
    c := New(start)
    wg := sync.WaitGroup{}
    log.Println(c.Count(&wg, finish, start))
}

Так панику не кидает, не правильно Wait использовал. Но не знаю так ли я понял структура-счетчик


Ответы (1 шт):

Автор решения: Maksim Fedorov

Первое — вы корректно сделали структуру счетчика

Но вы очень усложнили работу с счетчиком — какие-то finsih и start ввели зачем-то. У него простой интерфейс:

Inc() для инкремента и Value() для получения текущего значения

Остальное просто конкурентная среда: что-то работает с тяжелой работой, что-то считает результат этой работы... главное чтобы результат был корректным, то есть горутины не читали конкуретно состояние и одновременно не увеличивали (ну напрмиер сразу парочка/тройка горутин не увеличили состояние счетчика 5 до 6)

package main

import (
    "fmt"
    "log"
    "sync"
)

type Counter struct {
    num int
    sync.Mutex
}

func (c *Counter) Inc() {
    c.Lock()
    defer c.Unlock()

    c.num += 1
}

func (c *Counter) Value() int {
    return c.num
}

func main() {
    cnt := &Counter{
        num: 0,
    }

    finish := make(chan struct{})

    go Do(cnt, finish)

    select {
    case <-finish:
        log.Printf("Work done with count: %d", cnt.Value())
    }
}

func Do(cnt *Counter, finish chan struct{}) {
    wg := sync.WaitGroup{}

    // Hard work in goroutine and finish work
    for i := 0; i < 20; i++ {
        wg.Add(1)

        // Тяжелая работа
        go func(num int, cnt *Counter, wg *sync.WaitGroup) {
            defer wg.Done()

            fmt.Printf("Worker %d starting\n", num)
            cnt.Inc()
            fmt.Printf("Worker %d done\n", num)
        }(i, cnt, &wg)
    }

    wg.Wait()
    finish <- struct{}{}
    close(finish)
}

https://go.dev/play/p/mb9NGCVBC-K

→ Ссылка