From 42bc3554d4e8510fa2bddcdc8bc85238abf67c1f Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Mon, 31 Aug 2015 12:39:19 +0200 Subject: [PATCH 1/5] 02.6 --- ru/02.6.md | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 ru/02.6.md diff --git a/ru/02.6.md b/ru/02.6.md new file mode 100644 index 00000000..df802618 --- /dev/null +++ b/ru/02.6.md @@ -0,0 +1,395 @@ +# 2.6 Интерфейсы + +## Интерфейсы + +Интерфейсы - одна из наиболее тончайших черт дизайна Go. После прочтения этого раздела Вы, скорее всего, будете впечатлены тем, как они реализованы в Go. + +### Что такое интерфейс + +В общих чертах интерфейс - это набор методов, которые используются для того, чтобы определить набор действий. + +Например, в предыдущем примере и у Student(студент), и у Employee(работник) есть метод `SayHi()`, но они делают не одно и то же. + +Давайте кое-что сделаем. Мы добавим им еще один метод `Sing()`(Петь), а также метод `BorrowMoney()`(Взять взаймы денег) типу Student и метод `SpendSalary()`(Потратить з/плату) типу Employee. + +Теперь у Student есть три метода: `SayHi()`, `Sing()` и `BorrowMoney()`, а у Employee есть `SayHi()`, `Sing()` и `SpendSalary()`. + +Такая комбинация методов называется интерфейсом и реализуется как Student, так и Employee. Итак, Student и Employee реализуют интерфейс: `SayHi()` и `Sing()`. В то же время Employee не реализует интерфейс: `SayHi()`, `Sing()`, `BorrowMoney()`, а Student не реализует интерфейс: `SayHi()`, `Sing()`, `SpendSalary()`. Это потому, что у Employee нет метода `BorrowMoney()`, а у Student нет метода `SpendSalary()`. + +### Тип "Interface" + +Интерфейс определяет набор методов, поэтому, если тип реализует эти методы, говорится, что он реализует интерфейс. + + type Human struct { + name string + age int + phone string + } + + type Student struct { + Human + school string + loan float32 + } + + type Employee struct { + Human + company string + money float32 + } + + func (h *Human) SayHi() { + fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) + } + + func (h *Human) Sing(lyrics string) { + fmt.Println("Ля ля, ля ля ля, ля ля ля ля ля...", lyrics) + } + + func (h *Human) Guzzle(beerStein string) { + fmt.Println("Guzzle Guzzle Guzzle...", beerStein) + } + + // Employee перегружает метод SayHi + func (e *Employee) SayHi() { + fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, + e.company, e.phone) //Да, можно разбить строку на 2 строки. + } + + func (s *Student) BorrowMoney(amount float32) { + s.loan += amount // (снова и снова...) + } + + func (e *Employee) SpendSalary(amount float32) { + e.money -= amount + } + + // определяем интерфейс + type Men interface { + SayHi() + Sing(lyrics string) + Guzzle(beerStein string) + } + + type YoungChap interface { + SayHi() + Sing(song string) + BorrowMoney(amount float32) + } + + type ElderlyGent interface { + SayHi() + Sing(song string) + SpendSalary(amount float32) + } + +Интерфейс может быть реализован любым типом данных, и один тип может реализоваывать несколько интерфейсов одновременно. + +Заметьте, что все типы реализуют пустой интерфейс `interface{}`, так как у него нет методов, а все типы изначально также не имеют методов. + +### Значение интерфейса + +Итак, какие типы значений может принимать интерфейс? Если мы опрделеили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной. + +Как в примере выше, если мы определили переменную "m" как интерфейс Men, то все значения типа Student, Human или Employee могут быть присвоены переменной "m". Так что у нас может быть срез элементов типа Men, и значение любого типа, реализующего интерфейс Men, может присвоено элементам этого среза. Но имейте в виду, что срез элементов типа interface не ведет себя так же, как срез из элементов других типов. + + package main + + import "fmt" + + type Human struct { + name string + age int + phone string + } + + type Student struct { + Human + school string + loan float32 + } + + type Employee struct { + Human + company string + money float32 + } + + func (h Human) SayHi() { + fmt.Printf("Привет, я - %s, мой номер телефона - %s\n", h.name, h.phone) + } + + func (h Human) Sing(lyrics string) { + fmt.Println("Ля ля ля ля...", lyrics) + } + + func (e Employee) SayHi() { + fmt.Printf("Привет, я - %s, я работаю в %s. Звонгите мне по номеру %s\n", e.name, + e.company, e.phone) //Да, здесь можно разбить строку на две. + } + + // Интерфейс Men реализуется типами Human, Student и Employee + type Men interface { + SayHi() + Sing(lyrics string) + } + + func main() { + mike := Student{Human{"Майк", 25, "222-222-XXX"}, "MIT", 0.00} + paul := Student{Human{"Пол", 26, "111-222-XXX"}, "Harvard", 100} + sam := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Golang Inc.", 1000} + Tom := Employee{Human{"Сэм", 36, "444-222-XXX"}, "Things Ltd.", 5000} + + // определяем интерфейс i + var i Men + + //i может быть Student + i = mike + fmt.Println("Это Майк, студент:") + i.SayHi() + i.Sing("November rain") + + //i может быть Employee + i = Tom + fmt.Println("Это Том, работник:") + i.SayHi() + i.Sing("Born to be wild") + + // срез из элементов типа Men + fmt.Println("Давайте создадим срез из Men и посмотрим, что получится") + x := make([]Men, 3) + // Эти три элемента относятся к разным типам, но все они реализуют интерфейс Men + x[0], x[1], x[2] = paul, sam, mike + + for _, value := range x { + value.SayHi() + } + } + + +Интерфейс - это набор абстрактных методов, он может быть реализован типами, не являющимися интерфейсами. Поэтому он не может быть реализован самим собой. + +### Пустой интерфейс + +Пустой интерфейс - это интерфейс, который не содержит методов. Это очен полезно, если мы хотим хранить данные любого типа в одном месте, и это похоже на void* в C. + + // Определим a как пустой интерфейс + var a interface{} + var i int = 5 + s := "Привет, мир!" + // a может принимать значение любого типа + a = i + a = s + +Если функция использует пустой интерфейс в качестве входного аргумента, она может принимать значения любого типа; если функция использует пустой интерфейс в качестве возвращаемого значения, она может возвращать значения любого типа. + +### Интерфейсы как аргументы методов + +В интерфейсе может быть использована любая переменная. Как мы можем использовать это, чтобы передать переменную любого типа в функцию? + +Например, мы много используем fmt.Println, но Вы замечали, что эта команда может принимать в качестве аргумента данные любого типа? Заглянув в исходный код пакета fmt, мы можем найти следующее определение: + + type Stringer interface { + String() string + } + +Это значит, что любой тип, реализующий интерфейс Stringer, может быть передан в качестве аргумента в fmt.Println. Давайте докажем это: + + package main + + import ( + "fmt" + "strconv" + ) + + type Human struct { + name string + age int + phone string + } + + // Human реализует fmt.Stringer + func (h Human) String() string { + return "Имя:" + h.name + ", Возраст:" + strconv.Itoa(h.age) + " years, Контакт:" + h.phone + } + + func main() { + Bob := Human{"Боб", 39, "000-7777-XXX"} + fmt.Println("Этот человек: ", Bob) + } + + +Возвращаясь к примеру с Box можно обнаружить, что Color также реализует интерфейс Stringer, поэтому у нас есть возможность изменить формат вывода информации. Если не реализовать этот интерфейс, fmt.Println выведет тип на печать в формате по умолчанию. + + fmt.Println("Самая большая коробка: ", boxes.BiggestsColor().String()) + fmt.Println("Самая большая коробка: ", boxes.BiggestsColor()) + +Внимание: Если тип реализует интерфейс `error`, fmt вызовет `error()`, поэтому в этом случае Вам не надо реализовывать Stringer. + +### Тип переменной в интерфейсе + +Если тип переменной реализует интерфейс, то мы знаем, что значение любого типа, реализующего тот же самый интерфейс, может быть присвоено этой переменной. Но как мы узнаем, значение какого изначально типа присвоено этой переменной? Я покажу Вам два способа узнать это. + +- Подтверждение по шаблону запятая-ok + +В Go существует синтаксис `value, ok := element.(T)`. Так можно проверить, является ли переменная относящейся к указанному типу, где "value" - значение переменной, "ok" - это переменная булева типа, "element" - переменная типа interface и T - тип, который мы хотим подтвердить. + +Если element является переменной типа, который мы указали, ok будет равен true, иначе - false. + +Чтобы было понятнее, посмотрим на пример: + + package main + + import ( + "fmt" + "strconv" + ) + + type Element interface{} + type List []Element + + type Person struct { + name string + age int + } + + func (p Person) String() string { + return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" + } + + func main() { + list := make(List, 3) + list[0] = 1 // целочисленный тип + list[1] = "Привет" // строка + list[2] = Person{"Деннис", 70} + + for index, element := range list { + if value, ok := element.(int); ok { + fmt.Printf("list[%d] - это целое число, его значение - %d\n", index, value) + } else if value, ok := element.(string); ok { + fmt.Printf("list[%d] - это строка, его значение - %s\n", index, value) + } else if value, ok := element.(Person); ok { + fmt.Printf("list[%d] - это Person, его значение %s\n", index, value) + } else { + fmt.Printf("list[%d] - это данные какого-то другого типа\n", index) + } + } + } + +Пользоваться этим шаблоном довольно-таки просто, но если надо протестировать много типов, лучше воспользоваться `switch`. + +- тест с использованием switch + +Давайте перепишем наш пример с использованием `switch`. + + package main + + import ( + "fmt" + "strconv" + ) + + type Element interface{} + type List []Element + + type Person struct { + name string + age int + } + + func (p Person) String() string { + return "(Имя: " + p.name + " - возраст: " + strconv.Itoa(p.age) + " лет)" + } + + func main() { + list := make(List, 3) + list[0] = 1 // целое число + list[1] = "Hello" // строка + list[2] = Person{"Деннис", 70} + + for index, element := range list { + switch value := element.(type) { + case int: + fmt.Printf("list[%d] - целое число, его значение - %d\n", index, value) + case string: + fmt.Printf("list[%d] - строка, его значение - %s\n", index, value) + case Person: + fmt.Printf("list[%d] - Person, его значение - %s\n", index, value) + default: + fmt.Println("list[%d] - данные какого-то другого типа", index) + } + } + } + + +Нужно запомнить, что конструкция `element.(type)` не может быть использована вне тела `switch`, в этом случае надо использовать шаблон `запятая-ok`. + +### Встраиваемые интерфейсы + +В синтаксисе Go существует множество встроенной логики, такой, например, как анонимные поля в структуре. Неудивительно, что мы можем использовать в качестве анонимных полей и интерфейсы тоже, но называются они `Встроенные интерфейсы`. В этом случае мы следуем тем же правилам, что и в случае со встроенными полями. А точнее, если в интерфейс встроен другой интерфейс, то этот интерфейс будет иметь в себе все методы встроенного интерфейса. + +В исходном коде пакета `container/heap` мы можем видеть следующее определение: + + type Interface interface { + sort.Interface // встраиваемый sort.Interface + Push(x interface{}) // метод Push для того, чтобы помещать элементы в кучу + Pop() interface{} // метод Pop, который изымает элементы из кучи + } + +Мы видим, что `sort.Interface` является встраиваемым интерфейсом, поэтому в Interface неявно присутствуют три метода, содержащиеся внутри `sort.Interface`: + + type Interface interface { + // Len - количество элементов в коллекции + Len() int + // Less определяет, надо ли перемещать элемент с индексом i + // перед элементом с индексом j. + Less(i, j int) bool + // Swap меняем местами элементы с индексами i и j. + Swap(i, j int) + } + +Другой пример - `io.ReadWriter` из пакета `io`. + + // io.ReadWriter + type ReadWriter interface { + Reader + Writer + } + +### Рефлексия + +Рефлексия в Go используется для определения информации во время выполнения программы. Мы пользумеся пакетом `reflect`, и эта официальная [статья](http://golang.org/doc/articles/laws_of_reflection.html) объясняет, как reflect работает в Go. + +В процессе использования reflect задействованы 3 шага. Во-первых, нужно конвертировать интерфейс в типы reflect (reflect.Type или reflect.Value в зависимости от ситуации). + + t := reflect.TypeOf(i) // получает мета-данные типа i в переменную t + v := reflect.ValueOf(i) // получает значение типа i в переменную v + +После этого мы может конвертировать типы, полученные в результате рефлексии, для того, чтобы получить нужные нам значения. + + var x float64 = 3.4 + v := reflect.ValueOf(x) + fmt.Println("Тип:", v.Type()) + fmt.Println("Вид является float64:", v.Kind() == reflect.Float64) + fmt.Println("Значение:", v.Float()) + +Наконец, если мы хотим изменить значения типов, полученных в результате рефлексии, нам нужно сделать их изменяемыми. Как было обсуждено ранее, есть разница между передачей по ссылке и по значению. Следующий код не скомпилируется: + + var x float64 = 3.4 + v := reflect.ValueOf(x) + v.SetFloat(7.1) + +Вместо этого для изменения значений типов, полученных в результате рефлексии, нам нужно использовать следующий код: + + var x float64 = 3.4 + p := reflect.ValueOf(&x) + v := p.Elem() + v.SetFloat(7.1) + +Мы здесь обсудили основы рефлексии, однако, чтобы больше понять, Вы должны больше практиковаться. + +## Ссылки + +- [Содержание](preface.md) +- Предыдущий раздел: [Объектно-ориентированное программирование](02.5.md) +- Следующий раздел: [Многопоточность](02.7.md) From 7bab98ab984a3e1c6be2e61e3c2b545a32a54def Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Mon, 31 Aug 2015 12:46:39 +0200 Subject: [PATCH 2/5] 02.6 --- ru/02.6.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ru/02.6.md b/ru/02.6.md index df802618..874323dd 100644 --- a/ru/02.6.md +++ b/ru/02.6.md @@ -83,13 +83,13 @@ SpendSalary(amount float32) } -Интерфейс может быть реализован любым типом данных, и один тип может реализоваывать несколько интерфейсов одновременно. +Интерфейс может быть реализован любым типом данных, и один тип может реализовывать несколько интерфейсов одновременно. Заметьте, что все типы реализуют пустой интерфейс `interface{}`, так как у него нет методов, а все типы изначально также не имеют методов. ### Значение интерфейса -Итак, какие типы значений может принимать интерфейс? Если мы опрделеили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной. +Итак, какие типы значений может принимать интерфейс? Если мы определили переменную типа interface, то значение любого типа, который реализует этот интерфейс, может быть присвоено этой переменной. Как в примере выше, если мы определили переменную "m" как интерфейс Men, то все значения типа Student, Human или Employee могут быть присвоены переменной "m". Так что у нас может быть срез элементов типа Men, и значение любого типа, реализующего интерфейс Men, может присвоено элементам этого среза. Но имейте в виду, что срез элементов типа interface не ведет себя так же, как срез из элементов других типов. @@ -124,7 +124,7 @@ } func (e Employee) SayHi() { - fmt.Printf("Привет, я - %s, я работаю в %s. Звонгите мне по номеру %s\n", e.name, + fmt.Printf("Привет, я - %s, я работаю в %s. Звоните мне по номеру %s\n", e.name, e.company, e.phone) //Да, здесь можно разбить строку на две. } From 5acd831e237da6de2dfc4256f99a570c1e9b3c41 Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Tue, 1 Sep 2015 12:24:40 +0200 Subject: [PATCH 3/5] 02.7 --- ru/02.7.md | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 ru/02.7.md diff --git a/ru/02.7.md b/ru/02.7.md new file mode 100644 index 00000000..a65a48d5 --- /dev/null +++ b/ru/02.7.md @@ -0,0 +1,243 @@ +# Многопоточность + +Go называют C 21 века. Я думаю, этому есть две причины: во-первых, Go - простой язык, во-вторых, многопоточность сегодня является горячей темой, а Go поддерживает многопоточность на уровне языка. + +## Горутины + +Горутины и многопоточность встроены в ядро Go. Они подобны потокам, но работают по-другому. Больше дюжины горутин имеют в своей основе только 5-6 потоков. Go также дает в Ваше распоряжение полную поддержку расшаривания памяти в горутинах. Одна горутина обычно использует 4~5 килобайт памяти стека. Поэтому нетрудно запустить тысячи горутин на одном компьютере. Горутины более эффективны, более удобны в использовании и менее ресурсоемки, чем системные потоки. + +Горутины в Go запускаются на менеджере потоков во время выполнения. Чтобы создать новую горутину, которая на низлежащем уровне является функцией, используется ключевое слово `go`, ( ***main() - это горутина*** ). + + go hello(a, b, c) + +Давайте посмотрим на пример: + + package main + + import ( + "fmt" + "runtime" + ) + + func say(s string) { + for i := 0; i < 5; i++ { + runtime.Gosched() + fmt.Println(s) + } + } + + func main() { + go say("Мир") // create a new goroutine + say("Привет") // current goroutine + } + +Вывод: + + Привет + Мир + Привет + Мир + Привет + Мир + Привет + Мир + Привет + +Мы видим, что использовать многопоточность в Go с помощью ключевого слова `go` очень легко. В примере выше две горутины разделяют память, но лучше следовать следующему рецепту дизайна: не используйте разделяемые данные для коммуникаций, а используйте коммуникацию для того, чтобы разделять данные. + +runtime.Gosched() говорит процессору, что нужно исполнить другие горутины и вернуться затем назад. + +Для того, чтобы запустить все горутины, планировщик использует только один поток, что означает, что он один реализует многопоточность. Если для задействования преимущества параллельных процессов Вам надо использовать болше ядер процессора, Вам нужно вызвать runtime.GOMAXPROCS(n), чтобы установить количество ядер, которые Вы хотите использовать. Если `n<1`, эта команда ничего не меняет. В будущем эта функция может быть убрана, больше деталей о параллельных процессах и многопоточности смотрите в этой [статье](http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide). + +## Каналы + +Горутины выполняются в одном и том же адресном пространстве, поэтому, когда Вам нужен доступ к разделяемой памяти, Вам нужно организовывать синхронизацию. Как Вам осуществить коммуникации между различными горутинами? Для этого Go использует очень хороший механизм коммуникаций, называемый `канал`. `Канал` - это как двусторонняя "труба" в шеллах Unix: испольуйте `канал`, чтобы посылать и принимать данные. Единственный тип данных, используемый в каналах - это тип `channel` и ключевое слово `chan`. Помните, что для того, чтобы создать `канал`, нужно использовать `make`. + + ci := make(chan int) + cs := make(chan string) + cf := make(chan interface{}) + +Для того, чтобы посылать и принимать данные, `канал` использует оператор `<-`. + + ch <- v // посылаем v в канал ch. + v := <-ch // получаем данные из c, присваиваем их v + +Посмотрим еще на примеры: + + package main + + import "fmt" + + func sum(a []int, c chan int) { + total := 0 + for _, v := range a { + total += v + } + c <- total // посылаем total в c + } + + func main() { + a := []int{7, 2, 8, -9, 4, 0} + + c := make(chan int) + go sum(a[:len(a)/2], c) + go sum(a[len(a)/2:], c) + x, y := <-c, <-c // принимаем из c + + fmt.Println(x, y, x + y) + } + +Прием и передача данных по умолчанию блокирует канал, и это делает использование снихронных горутин намного легче. Под блокированием я имею в виду то, что горутина не продолжит свое выполнение, если пытается получить аднные из пустого канала, например, (`value := <-ch`), пока другие горутины не отправят данные в этот канал. С другой строны, горутина не продожит свое выполнение, пока данные, которые она послалав канал, например (`ch<-5`), не приняты. + +## Буферизованные каналы + +Выше я говорил о небуферизованных каналах. В Go также есть буферизованные каналы, которые могут хранить больше, чем один элемент. Нарпимер, `ch := make(chan bool, 4)`, здесь мы создали канал, который может содержать 4 булевых элемента. Поэтому в этот канал мы можем послать до 4 булевых элементов, и горутина не заблокриуется, но она заблокируется, когда Вы попытаетесь послать в канал пятый элемент, и ни одна горутина его не примет. + + ch := make(chan type, n) + + n == 0 ! не буферизованный(block) + n > 0 ! буферизованный (не заблокирует горутину, пока не получит n элементов) + +Вы можете попробовать запустить следующий код на своем компьютере и поменять какие-нибудь значения: + + package main + + import "fmt" + + func main() { + c := make(chan int, 2) // если изменить 2 на 1, то в процессе выполнения будет вызвана ошибка, но если на 3, то все будет в порядке + c <- 1 + c <- 2 + fmt.Println(<-c) + fmt.Println(<-c) + } + +## Range и Close + +Чтобы оперировать данными в буферизованных каналах так же, как в срезах или картах, мы можем использовать range: + + package main + + import ( + "fmt" + ) + + func fibonacci(n int, c chan int) { + x, y := 1, 1 + for i := 0; i < n; i++ { + c <- x + x, y = y, x + y + } + close(c) + } + + func main() { + c := make(chan int, 10) + go fibonacci(cap(c), c) + for i := range c { + fmt.Println(i) + } + } + +`for i := range c` не остановит чтение данных из канала, пока тот не будет закрыт. Чтобы закрыть канал в вышеприведенном примере мы используем ключевое слово `close`. Послать или принять данные из закрытого канала невозможно; чтобы проверить, закрыт ли канал, можно использовать команду `v, ok := <-ch`. Если `ok` вернул false, это означает, что в канале нет данных, и он был закрыт. + +Не забывайте закрывать каналы в горутинах, которые посылают данные, а не в тех, которые получают, иначе очень легко получить 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 в случайном порядке выбирает, с каким из них работать. + + package main + + import "fmt" + + func fibonacci(c, quit chan int) { + x, y := 1, 1 + for { + select { + case c <- x: + x, y = y, x + y + case <-quit: + fmt.Println("Выход") + return + } + } + } + + func main() { + c := make(chan int) + quit := make(chan int) + go func() { + for i := 0; i < 10; i++ { + fmt.Println(<-c) + } + quit <- 0 + }() + fibonacci(c, quit) + } + +У `select` есть также поведение по умолчанию `default`, так же, как и у `switch`. Если ни один их каналов не готов к использованию, исполняется случай по умолчанию (при этом больше не ожидается, пока канал будет готов). + + select { + case i := <-c: + // use i + default: + // выполняется, когда c заблокирован + } + +## Тайм-аут + +Иногда горутина блокируется. Как избежать того, чтобы это заблокировало всю программу? Это просто, можно установить тайм-аут в select: + + func main() { + c := make(chan int) + o := make(chan bool) + go func() { + for { + select { + case v := <- c: + println(v) + case <- time.After(5 * time.Second): + println("Тайм-аут") + o <- true + break + } + } + }() + <- o + } + +## Runtime goroutine + +В пакете `runtime` есть несколько функций для работы с горутинами. + +- `runtime.Goexit()` + + Прекращает работу текущей горутины, но функции после слова defer будут выполнены в обычном порядке. + +- `runtime.Gosched()` + + Позволяет планировщику выполнить остальные горутины и затем продолжает выполнение. + +- `runtime.NumCPU() int` + + Возвращает количество ядер процессора + +- `runtime.NumGoroutine() int` + + Возвращает количество горутин + +- `runtime.GOMAXPROCS(n int) int` + + Устанавливает количество ядер процессора, которые Вы хотите использовать + +## Ссылки + +- [Содержание](preface.md) +- Предыдущий раздел: [Интерфейсы](02.6.md) +- Следующий раздел: [Итоги раздела](02.8.md) From 08654f41824070b96842badb8ec3e9c3aa542945 Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Tue, 1 Sep 2015 12:38:47 +0200 Subject: [PATCH 4/5] 02.7 --- ru/02.7.md | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ru/02.7.md b/ru/02.7.md index a65a48d5..b2cb77f2 100644 --- a/ru/02.7.md +++ b/ru/02.7.md @@ -4,7 +4,7 @@ Go называют C 21 века. Я думаю, этому есть две п ## Горутины -Горутины и многопоточность встроены в ядро Go. Они подобны потокам, но работают по-другому. Больше дюжины горутин имеют в своей основе только 5-6 потоков. Go также дает в Ваше распоряжение полную поддержку расшаривания памяти в горутинах. Одна горутина обычно использует 4~5 килобайт памяти стека. Поэтому нетрудно запустить тысячи горутин на одном компьютере. Горутины более эффективны, более удобны в использовании и менее ресурсоемки, чем системные потоки. +Горутины и многопоточность встроены в ядро Go. Они подобны потокам, но работают по-другому. Больше дюжины горутин будут иметь в своей основе только 5-6 потоков. Go также дает в Ваше распоряжение полную поддержку расшаривания памяти в горутинах. Одна горутина обычно использует 4~5 килобайт памяти стека. Поэтому нетрудно запустить тысячи горутин на одном компьютере. Горутины более эффективны, более удобны в использовании и менее ресурсоемки, чем системные потоки. Горутины в Go запускаются на менеджере потоков во время выполнения. Чтобы создать новую горутину, которая на низлежащем уровне является функцией, используется ключевое слово `go`, ( ***main() - это горутина*** ). @@ -27,8 +27,8 @@ Go называют C 21 века. Я думаю, этому есть две п } func main() { - go say("Мир") // create a new goroutine - say("Привет") // current goroutine + go say("Мир") // создает новую горутину + say("Привет") // текущая горутина } Вывод: @@ -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) @@ -87,7 +87,7 @@ runtime.Gosched() говорит процессору, что нужно исп fmt.Println(x, y, x + y) } -Прием и передача данных по умолчанию блокирует канал, и это делает использование снихронных горутин намного легче. Под блокированием я имею в виду то, что горутина не продолжит свое выполнение, если пытается получить аднные из пустого канала, например, (`value := <-ch`), пока другие горутины не отправят данные в этот канал. С другой строны, горутина не продожит свое выполнение, пока данные, которые она послалав канал, например (`ch<-5`), не приняты. +Прием и передача данных по умолчанию блокирует канал, и это делает использование синхронных горутин намного легче. Под блокированием я имею в виду то, что горутина не продолжит свое выполнение, если пытается получить данные из пустого канала, например, (`value := <-ch`), пока другие горутины не отправят данные в этот канал. С другой строны, горутина не продожит свое выполнение, пока данные, которые она послала в канал, например (`ch<-5`), не приняты. ## Буферизованные каналы @@ -95,8 +95,8 @@ runtime.Gosched() говорит процессору, что нужно исп ch := make(chan type, n) - n == 0 ! не буферизованный(block) - n > 0 ! буферизованный (не заблокирует горутину, пока не получит n элементов) + n == 0 ! не буферизованный(блокирует выполнение горутины) + n > 0 ! буферизованный (не заблокирует выполнение, пока не получит n элементов) Вы можете попробовать запустить следующий код на своем компьютере и поменять какие-нибудь значения: @@ -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) @@ -141,7 +141,7 @@ runtime.Gosched() говорит процессору, что нужно исп `for i := range c` не остановит чтение данных из канала, пока тот не будет закрыт. Чтобы закрыть канал в вышеприведенном примере мы используем ключевое слово `close`. Послать или принять данные из закрытого канала невозможно; чтобы проверить, закрыт ли канал, можно использовать команду `v, ok := <-ch`. Если `ok` вернул false, это означает, что в канале нет данных, и он был закрыт. -Не забывайте закрывать каналы в горутинах, которые посылают данные, а не в тех, которые получают, иначе очень легко получить panic. +Не забывайте закрывать каналы в горутинах, которые посылают данные, а не в тех, которые получают, иначе очень легко получить состояние "panic". Другое, что надо помнить о каналах - это то, что каналы не подобны файлам. Вам не нужно закрывать их, пока Вы не придете к выводу, что канал больше не нужен, или Вы хотите выйти из цикла range. @@ -149,8 +149,7 @@ runtime.Gosched() говорит процессору, что нужно исп В примерах выше мы использовали только один канал, но что, если мы имеем дело более чем с одним каналом? В 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 @@ -212,17 +211,17 @@ runtime.Gosched() говорит процессору, что нужно исп <- o } -## Runtime goroutine +## Runtime и горутины -В пакете `runtime` есть несколько функций для работы с горутинами. +В пакете `runtime` есть несколько функций для работы с горутинами: - `runtime.Goexit()` - Прекращает работу текущей горутины, но функции после слова defer будут выполнены в обычном порядке. + Прекращает работу текущей горутины, но функции после слова defer будут выполнены в обычном порядке - `runtime.Gosched()` - Позволяет планировщику выполнить остальные горутины и затем продолжает выполнение. + Позволяет планировщику выполнить остальные горутины и затем продолжает выполнение - `runtime.NumCPU() int` From 1c3231a457f4a63701a1b724409a44a9ad7db07c Mon Sep 17 00:00:00 2001 From: Slava Zgordan Date: Wed, 2 Sep 2015 10:10:06 +0200 Subject: [PATCH 5/5] 02.8 --- ru/02.2.md | 2 +- ru/02.8.md | 32 ++++++++++++++++++++++++++++++++ ru/preface.md | 12 ++++++------ 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 ru/02.8.md diff --git a/ru/02.2.md b/ru/02.2.md index 0b01dbb5..9c5d1a4f 100644 --- a/ru/02.2.md +++ b/ru/02.2.md @@ -1,4 +1,4 @@ -# 2.2 Фундамент Go +# 2.2 Основы Go В этом разделе мы научим Вас тому, как определять константы, переменные, относящиеся к элементарным типам данных, а также некоторым приемам программирования на Go. diff --git a/ru/02.8.md b/ru/02.8.md new file mode 100644 index 00000000..ad0e277a --- /dev/null +++ b/ru/02.8.md @@ -0,0 +1,32 @@ +# 2.8 Итоги раздела + +В этом разделе мы познакомили Вас с 25 ключевыми словами Go. Давайте снова посмотрим на них: + + break default func interface select + case defer go map struct + chan else goto package switch + const fallthrough if range type + continue for import return var + +- `var` и `const` используются для определения переменных и констант. +- `package` и `import` используются для работы с пакетами. +- `func` используется для определения функций и методов. +- `return` используется для того, чтобы возвращать данные из функций и методов. +- `defer` используется для определения отложенных функций. +- `go` используется для того, чтобы начать выполнение новой горутины. +- `select` используется для того, чтобы переключаться между несколькими каналами для коммуникации с ними. +- `interface` используется для того, чтобы определять интерфейсы. +- `struct` используется для того, чтобы определять пользовательские типы. +- `break`, `case`, `continue`, `for`, `fallthrough`, `else`, `if`, `switch`, `goto` и `default` были представлены в разделе 2.3. +- `chan` - это тип данных "канал", который служит для того, чтобы осуществлять коммуникации между горутинами. +- `type` нужен для того, чтобы определять пользовательские типы. +- `map` используется для того, чтобы определять карты, которые подобны хэш-таблицам в других языках программирования. +- `range` используется для того, чтобы читать данные из `срезов`, `карт` и `каналов`. + +Если Вы поняли, как пользоваться этими 25 ключевыми словами, Вы уже изучили многое из Go. + +## Ссылки + +- [Содержание](preface.md) +- Предыдущий раздел: [Многопоточность](02.7.md) +- Следующий раздел: [Основы Веба](03.0.md) diff --git a/ru/preface.md b/ru/preface.md index 7ee615cc..c512586f 100644 --- a/ru/preface.md +++ b/ru/preface.md @@ -4,15 +4,15 @@ - 1.3. [Утилиты Go](01.3.md) - 1.4. [Инструменты разработки для Go](01.4.md) - 1.5. [Итоги раздела](01.5.md) -- 2.[Основы Go](02.0.md) - - 2.1. ["Hello, Go"](02.1.md) - - 2.2. [Фундамент Go](02.2.md) +- 2.[Введение в Go](02.0.md) + - 2.1. ["Привет, Go"](02.1.md) + - 2.2. [Основы Go](02.2.md) - 2.3. [Управляющие конструкции и функции](02.3.md) - 2.4. [Структуры](02.4.md) - 2.5. [Объектно-ориентированное программирование](02.5.md) - - 2.6. [interface](02.6.md) - - 2.7. [Concurrency](02.7.md) - - 2.8. [Summary](02.8.md) + - 2.6. [Интерфейсы](02.6.md) + - 2.7. [Многопоточность](02.7.md) + - 2.8. [Итоги раздела](02.8.md) - 3.[Web foundation](03.0.md) - 3.1. [Web working principles](03.1.md) - 3.2. [Build a simple web server](03.2.md)