Вот и подошло время познакомиться с самой сильной стороной языка Go — горутинами. Горутины — это легковесные потоки, которые реализуют конкурентное программирование в Go. Их называют легковесными потоками, потому что они управляются рантаймом языка, а не операционной системой. Стоимость переключения контекста и расход памяти намного ниже, чем у потоков ОС. Следовательно, для Go — не проблема поддерживать одновременно десятки тысяч горутин.
Запустить функцию в горутине — супер легко. Для этого достаточно написать слово go
перед вызовом функции:
package main
import (
"fmt"
"time"
)
func main() {
// выведет сообщение в горутине
go fmt.Println("Hello concurrent world")
// если не подождать, то программа закончится, не успев, вывести сообщение
time.Sleep(100 * time.Millisecond)
}
При написании конкурентного кода возникают новые моменты, которые нужно учитывать: состояние гонки, блокировки, коммуникация между горутинами. Пример программы, которая работает не так, как ожидается:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(100 * time.Millisecond)
}
Сперва может показаться, что должны вывестись числа от 0 до 4, но на самом деле вывод будет следующим:
5
5
5
5
5
Все потому что i передается в общем скоупе, следовательно, когда горутины будут выполняться, цикл уже закончится и i будет равно 5. В данном случае нужно передать копию i:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
time.Sleep(100 * time.Millisecond)
}
Вывод:
0
4
3
1
2
Также можно заметить, что числа вывелись не в порядке вызова. Горутины выполняются независимо и не гарантируют порядка. При необходимости последовательность в выполнении придется реализовывать самостоятельно.
Реализуйте функцию MaxSum(nums1, nums2 []int) []int
из прошлого задания, используя горутины для расчета каждой суммы слайса.
Не забудьте использовать функцию time.Sleep(100 * time.Millisecond)
, чтобы сумма успела посчитаться. В настоящих приложениях используются специальные инструменты, чтобы дожидаться исполнения асинхронного кода, но для простоты здесь будет использоваться обычный сон.
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1package solution
2
3import (
4 "github.com/stretchr/testify/assert"
5 "testing"
6)
7
8func TestMaxSum(t *testing.T) {
9 a := assert.New(t)
10 a.Nil(MaxSum(nil, nil))
11 a.Equal([]int{3, 4, 5, 6}, MaxSum([]int{1, 2, 3}, []int{3, 4, 5, 6}))
12 a.Equal([]int{1, 2, 3}, MaxSum([]int{1, 2, 3}, []int{3, 2, 1}))
13 a.Equal([]int{10, 20}, MaxSum([]int{10, 20}, []int{4, 6}))
14}
15
Решение учителя откроется через: