Предположим, у меня есть следующая структура:
package manager
type Manager struct {
strings []string
}
func (m *Manager) AddString(s string) {
m.strings = append(m.strings, s)
}
func (m *Manager) RemoveString(s string) {
for i, str := range m.strings {
if str == s {
m.strings = append(m.strings[:i], m.strings[i+1:]...)
}
}
}
Этот шаблон не является потокобезопасным, поэтому следующий тест не пройден из-за некоторого состояния гонки (индекс массива вне границ):
func TestManagerConcurrently(t *testing.T) {
m := &manager.Manager{}
wg := sync.WaitGroup{}
for i:=0; i<100; i++ {
wg.Add(1)
go func () {
m.AddString("a")
m.AddString("b")
m.AddString("c")
m.RemoveString("b")
wg.Done()
} ()
}
wg.Wait()
fmt.Println(m)
}
Я новичок в Go, и из-за того, что гуглюсь вокруг, я полагаю, что должен использовать каналы (?) Таким образом, один из способов сделать это параллельным будет выглядеть так:
type ManagerA struct {
Manager
addStringChan chan string
removeStringChan chan string
}
func NewManagerA() *ManagerA {
ma := &ManagerA{
addStringChan: make(chan string),
removeStringChan: make(chan string),
}
go func () {
for {
select {
case msg := <-ma.addStringChan:
ma.AddString(msg)
case msg := <-ma.removeStringChan:
ma.RemoveString(msg)
}
}
}()
return ma
}
func (m* ManagerA) AddStringA(s string) {
m.addStringChan <- s
}
func (m* ManagerA) RemoveStringA(s string) {
m.removeStringChan <- s
}
Я хотел бы представить API, аналогичный непараллельному примеру, следовательно, AddStringA, RemoveStringA.
Это кажется работать как ожидалось одновременно (хотя я предполагаю, что внутренний goroutine должен также выйти в некоторый момент). Моя проблема с этим заключается в том, что существует множество дополнительных шаблонов:
- необходимо определить и инициализировать каналы
- определить внутреннюю последовательность l oop с помощью select
- сопоставить функции с вызовами канала
Мне кажется, это немного. Есть ли способ упростить это (рефакторинг / синтаксис / библиотека)?
Я думаю, что лучший способ реализовать это - использовать вместо этого Mutex? Но можно ли все еще упростить этот типовой шаблон?