2017-02-14 22 views
0

Скажем, у меня есть следующий код:Golang мьютекс пробегает общий массив в goroutines

a := []int{1,2,3} 
i := 0 
var mu = &sync.Mutex{} 
for i < 10 { 
    go func(a *[]int) { 
     for _, i := range a { 
      mu.Lock() 
      fmt.Println(a[0]) 
      mu.Unlock() 
     } 
    }(&a) 
    i++ 
} 

Массив представляет собой совместно используемый ресурс, и считывается из в цикле. Как защитить массив в заголовке цикла и мне нужно? Также необходимо передать массив в goroutine как указатель?

ответ

2

Во-первых, некоторые Go терминологии:

[]int{1, 2, 3} является ломтик, не массив. Массив будет записан как [...]int{1, 2, 3}.

Кусочек триплет (start, length, capacity) и указывает на базовый массив (обычно кучного выделено, но это деталь реализации, что язык полностью скрывает от вас!)

модель памяти Go позволяет любое количество читателей или (но не и) не более одного писателя в любой заданный регион в памяти. Go memory model (к сожалению) конкретно не вызывает случай одновременного доступа к нескольким индексам в один и тот же срез, но, как представляется, это нормально (т. Е. Они рассматриваются как разные места в памяти, как и следовало ожидать).

Так что, если вы просто читаете оттуда, нет необходимости защищать его вообще.

Если вы читаете и писать к нему, но goroutines не читать и писать в одних и тех же местах, как друг с другом (например, если goroutine i только считывает и записывает в положение i), то вы также не требуется синхронизация. Кроме того, вы можете либо синхронизировать весь фрагмент (что означает меньшее количество мьютексов, но много), или вы могли бы синхронизировать отдельные позиции в срезе (что означает гораздо более низкую конкуренцию, но многие другие мьютексы и блокировки были приобретены и выпущены).

Но поскольку Go позволяет функции захвата переменных в объеме (то есть, они являются замыканиями) там действительно нет причин, чтобы передать массив в качестве указателя на всех:

Ваш код будет, таким образом, наиболее идиоматически быть записана в виде :

a := []int{1,2,3} 
for i := 0; i < 10; i++ 
for i < 10 { 
    go func() { 
     for _, i := range a { 
      fmt.Println(a[0]) 
     } 
    }() 
} 

Я не совсем уверен, что приведенный выше код должен быть для-, так как он собирается распечатать a[0] 10 раз в различных goroutines, что делает его похожим на это даже не используя кусочек в осмысленно.

+0

Условное объяснение. Благодаря! Код является всего лишь примером, поэтому я могу понять, как работают указатели, массивы и блокировки. Кажется, я часто сталкиваюсь с этим конкретным шаблоном. В любом случае, еще раз спасибо. – Soubriquet

+0

Одно из предостережений заключается в том, что существует распространенная ошибка (которая обнаруживает 'go vet', поэтому я бы рекомендовал работать) состоит в том, что переменные цикла (например,' i' в 'range') имеют область, которая длится дольше, чем заданная итерация ; в частности: 'для i: = диапазон a {go func() {fmt.Printf ("% d ", i)}()}' , вероятно, распечатает 3, 3, 3 или различные другие значения поскольку все горуты захватывают один и тот же «i». Решение состоит в том, чтобы сделать 'для i: = range a {go func (i int) {fmt.Printf ("% d ", i)} (i)}' – TaslemGuy

+0

Что значит, что они длятся дольше? Они все еще только в области цикла, верно? – Soubriquet

2

Первое, что вы знаете shuold a := []int{1,2,3} - это не массив, это ломтик.

Литеральный фрагмент как массивный литерал без длины.

Это массив литерал:

[3]bool{true, true, false} 

И это создает тот же массив, как описано выше, затем строит кусочек, который ссылается на нее:

[]bool{true, true, false} 

Типы с пустым [], такие как [] int, на самом деле являются срезами, а не массивами. В Go размер массива является частью типа, поэтому для фактического наличия массива вам нужно будет иметь что-то вроде [16] int, а указателем на это будет * [16] int.

Вопрос: необходимо ли передать массив в goroutine в качестве указателя?

A: Нет От https://golang.org/doc/effective_go.html#slices

Если функция принимает аргумент среза, изменяет это делает к элементам из среза будет виден вызывающему абоненту, по аналогии с пропусканием указатель на лежащий в основе массив.

 Смежные вопросы

  • Нет связанных вопросов^_^