Skip to content

Commit

Permalink
02.7
Browse files Browse the repository at this point in the history
  • Loading branch information
Slava Zgordan committed Sep 1, 2015
1 parent 5acd831 commit 08654f4
Showing 1 changed file with 16 additions and 17 deletions.
33 changes: 16 additions & 17 deletions ru/02.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Go называют C 21 века. Я думаю, этому есть две п

## Горутины

Горутины и многопоточность встроены в ядро Go. Они подобны потокам, но работают по-другому. Больше дюжины горутин имеют в своей основе только 5-6 потоков. Go также дает в Ваше распоряжение полную поддержку расшаривания памяти в горутинах. Одна горутина обычно использует 4~5 килобайт памяти стека. Поэтому нетрудно запустить тысячи горутин на одном компьютере. Горутины более эффективны, более удобны в использовании и менее ресурсоемки, чем системные потоки.
Горутины и многопоточность встроены в ядро Go. Они подобны потокам, но работают по-другому. Больше дюжины горутин будут иметь в своей основе только 5-6 потоков. Go также дает в Ваше распоряжение полную поддержку расшаривания памяти в горутинах. Одна горутина обычно использует 4~5 килобайт памяти стека. Поэтому нетрудно запустить тысячи горутин на одном компьютере. Горутины более эффективны, более удобны в использовании и менее ресурсоемки, чем системные потоки.

Горутины в Go запускаются на менеджере потоков во время выполнения. Чтобы создать новую горутину, которая на низлежащем уровне является функцией, используется ключевое слово `go`, ( ***main() - это горутина*** ).

Expand All @@ -27,8 +27,8 @@ Go называют C 21 века. Я думаю, этому есть две п
}

func main() {
go say("Мир") // create a new goroutine
say("Привет") // current goroutine
go say("Мир") // создает новую горутину
say("Привет") // текущая горутина
}

Вывод:
Expand All @@ -43,15 +43,15 @@ Go называют C 21 века. Я думаю, этому есть две п
Мир
Привет

Мы видим, что использовать многопоточность в Go с помощью ключевого слова `go` очень легко. В примере выше две горутины разделяют память, но лучше следовать следующему рецепту дизайна: не используйте разделяемые данные для коммуникаций, а используйте коммуникацию для того, чтобы разделять данные.
Мы видим, что реализовывать многопоточность в Go с помощью ключевого слова `go` очень легко. В примере выше две горутины разделяют память, но лучше следовать следующему рецепту: не используйте разделяемые данные для коммуникаций, а используйте коммуникации для того, чтобы разделять данные.

runtime.Gosched() говорит процессору, что нужно исполнить другие горутины и вернуться затем назад.

Для того, чтобы запустить все горутины, планировщик использует только один поток, что означает, что он один реализует многопоточность. Если для задействования преимущества параллельных процессов Вам надо использовать болше ядер процессора, Вам нужно вызвать runtime.GOMAXPROCS(n), чтобы установить количество ядер, которые Вы хотите использовать. Если `n<1`, эта команда ничего не меняет. В будущем эта функция может быть убрана, больше деталей о параллельных процессах и многопоточности смотрите в этой [статье](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).
Для того, чтобы запустить все горутины, планировщик использует только один поток, что означает, что он один реализует многопоточность. Если для задействования преимущества параллельных процессов Вам надо использовать больше ядер процессора, Вам нужно вызвать runtime.GOMAXPROCS(n), чтобы установить количество ядер, которые Вы хотите использовать. Если `n<1`, эта команда ничего не меняет. В будущем эта функция может быть убрана, больше деталей о параллельных процессах и многопоточности смотрите в этой [статье](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide).

## Каналы

Горутины выполняются в одном и том же адресном пространстве, поэтому, когда Вам нужен доступ к разделяемой памяти, Вам нужно организовывать синхронизацию. Как Вам осуществить коммуникации между различными горутинами? Для этого Go использует очень хороший механизм коммуникаций, называемый `канал`. `Канал` - это как двусторонняя "труба" в шеллах Unix: испольуйте `канал`, чтобы посылать и принимать данные. Единственный тип данных, используемый в каналах - это тип `channel` и ключевое слово `chan`. Помните, что для того, чтобы создать `канал`, нужно использовать `make`.
Горутины выполняются в одном и том же адресном пространстве, поэтому, когда Вам нужен доступ к разделяемой памяти, Вам нужно организовывать синхронизацию. Как осуществлять коммуникации между различными горутинами? Для этого Go использует очень хороший механизм коммуникаций, называемый `канал`. `Канал` - это как двусторонняя "труба" в шеллах Unix: используйте `канал`, чтобы посылать и принимать данные. Единственный тип данных, используемый в каналах - это тип `channel` и ключевое слово `chan`. Помните, что для того, чтобы создать `канал`, нужно использовать `make`.

ci := make(chan int)
cs := make(chan string)
Expand Down Expand Up @@ -87,16 +87,16 @@ runtime.Gosched() говорит процессору, что нужно исп
fmt.Println(x, y, x + y)
}

Прием и передача данных по умолчанию блокирует канал, и это делает использование снихронных горутин намного легче. Под блокированием я имею в виду то, что горутина не продолжит свое выполнение, если пытается получить аднные из пустого канала, например, (`value := <-ch`), пока другие горутины не отправят данные в этот канал. С другой строны, горутина не продожит свое выполнение, пока данные, которые она послалав канал, например (`ch<-5`), не приняты.
Прием и передача данных по умолчанию блокирует канал, и это делает использование синхронных горутин намного легче. Под блокированием я имею в виду то, что горутина не продолжит свое выполнение, если пытается получить данные из пустого канала, например, (`value := <-ch`), пока другие горутины не отправят данные в этот канал. С другой строны, горутина не продожит свое выполнение, пока данные, которые она послала в канал, например (`ch<-5`), не приняты.

## Буферизованные каналы

Выше я говорил о небуферизованных каналах. В Go также есть буферизованные каналы, которые могут хранить больше, чем один элемент. Нарпимер, `ch := make(chan bool, 4)`, здесь мы создали канал, который может содержать 4 булевых элемента. Поэтому в этот канал мы можем послать до 4 булевых элементов, и горутина не заблокриуется, но она заблокируется, когда Вы попытаетесь послать в канал пятый элемент, и ни одна горутина его не примет.

ch := make(chan type, n)

n == 0 ! не буферизованный(block
n > 0 ! буферизованный (не заблокирует горутину, пока не получит n элементов)
n == 0 ! не буферизованный(блокирует выполнение горутины
n > 0 ! буферизованный (не заблокирует выполнение, пока не получит n элементов)

Вы можете попробовать запустить следующий код на своем компьютере и поменять какие-нибудь значения:

Expand All @@ -105,7 +105,7 @@ runtime.Gosched() говорит процессору, что нужно исп
import "fmt"

func main() {
c := make(chan int, 2) // если изменить 2 на 1, то в процессе выполнения будет вызвана ошибка, но если на 3, то все будет в порядке
c := make(chan int, 2) // если изменить 2 на 1, то в процессе выполнения будет вызвана ошибка, но если заменить 2 на 3, то все будет в порядке
c <- 1
c <- 2
fmt.Println(<-c)
Expand Down Expand Up @@ -141,16 +141,15 @@ runtime.Gosched() говорит процессору, что нужно исп

`for i := range c` не остановит чтение данных из канала, пока тот не будет закрыт. Чтобы закрыть канал в вышеприведенном примере мы используем ключевое слово `close`. Послать или принять данные из закрытого канала невозможно; чтобы проверить, закрыт ли канал, можно использовать команду `v, ok := <-ch`. Если `ok` вернул false, это означает, что в канале нет данных, и он был закрыт.

Не забывайте закрывать каналы в горутинах, которые посылают данные, а не в тех, которые получают, иначе очень легко получить panic.
Не забывайте закрывать каналы в горутинах, которые посылают данные, а не в тех, которые получают, иначе очень легко получить состояние "panic".

Другое, что надо помнить о каналах - это то, что каналы не подобны файлам. Вам не нужно закрывать их, пока Вы не придете к выводу, что канал больше не нужен, или Вы хотите выйти из цикла range.

## Select

В примерах выше мы использовали только один канал, но что, если мы имеем дело более чем с одним каналом? В Go есть ключевое слово, называемое `select`, используемое для того, чтобы слушать много каналов.

`select` is blocking by default and it continues to execute only when one of channels has data to send or receive. If several channels are ready to use at the same time, select chooses which to execute randomly.
`select` по умолчанию блокирует дальнейшее выполнение и продолжает его лишь только когда в одном из каналов появляются данные для получения или отправки. Если несколько каналов готовы принять или отправить данные одновременно, select в случайном порядке выбирает, с каким из них работать.
`select` по умолчанию блокирует дальнейшее выполнение и продолжает его лишь только тогда, когда в одном из каналов появляются данные для получения или отправки. Если несколько каналов готовы принять или отправить данные одновременно, select в случайном порядке выбирает, с каким из них работать.

package main

Expand Down Expand Up @@ -212,17 +211,17 @@ runtime.Gosched() говорит процессору, что нужно исп
<- o
}

## Runtime goroutine
## Runtime и горутины

В пакете `runtime` есть несколько функций для работы с горутинами.
В пакете `runtime` есть несколько функций для работы с горутинами:

- `runtime.Goexit()`

Прекращает работу текущей горутины, но функции после слова defer будут выполнены в обычном порядке.
Прекращает работу текущей горутины, но функции после слова defer будут выполнены в обычном порядке

- `runtime.Gosched()`

Позволяет планировщику выполнить остальные горутины и затем продолжает выполнение.
Позволяет планировщику выполнить остальные горутины и затем продолжает выполнение

- `runtime.NumCPU() int`

Expand Down

0 comments on commit 08654f4

Please sign in to comment.