Блокировка должна покрывать и чтение seen, и добавление ключа. Иначе остаётся data race, а два работника могут одновременно решить, что число ещё не встречалось.
Получение уникальных значений массива через горутины
Блокировка должна покрывать и чтение seen, и добавление ключа. Иначе остаётся data race, а два работника могут одновременно решить, что число ещё не встречалось.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Требуется использовать несколько горутин для получения только уникальных элементов из сгенерированного массива, при этом исключить дублирование результатов и отправить найденные уникальные числа в канал. Необходимо обеспечить корректную синхронизацию доступа к общему хранищу уже встреченных значений, чтобы избежать гонок данных.
func main() {
var seen = make(map[int]struct{})
lock := sync.Mutex{}
size := 1000
numbers := make([]int, 0, size)
for i := 0; i < size; i++ {
numbers = append(numbers, rand.Intn(10)) // generate random ints
}
uniqChan := make(chan int, size)
wg := sync.WaitGroup{}
for i := 0; i < size; i++ {
i := i
wg.Add(1)
go func() {
defer wg.Done()
if _, ok := seen[numbers[i]]; !ok {
lock.Lock()
seen[numbers[i]] = struct{}{}
lock.Unlock()
uniqChan <- numbers[i]
}
}()
}
wg.Wait()
close(uniqChan)
for val := range uniqChan {
fmt.Println(val)
}
fmt.Println(uniqChan)
}
Ответ
Блокировка должна покрывать и чтение seen, и добавление ключа. Иначе остаётся data race, а два работника могут одновременно решить, что число ещё не встречалось.
Внутри горутины замените проверку на:
value := numbers[i]
lock.Lock()
_, exists := seen[value]
if !exists { seen[value] = struct{}{} }
lock.Unlock()
if !exists { uniqChan <- value }
WaitGroup и закрытие после Wait оставьте. Буфер size достаточен для отложенного чтения в данном примере; с маленьким буфером читатель должен работать одновременно. fmt.Println(uniqChan) печатает значение канала, не его содержимое. Порядок уникальных чисел не задан; тысяча горутин для такой простой операции избыточна.