Основы горутин
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 59 из 107.
Горутина (goroutine) — это легковесный поток выполнения, управляемый средой выполнения Go. В отличие от традиционных потоков, которые потребляют значительный объем памяти, горутины запускаются всего с несколькими килобайтами стековой памяти, что позволяет запускать тысячи таких потоков одновременно.
Чтобы запустить горутину, просто добавьте ключевое слово go перед вызовом функции:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // выполняется конкурентно
go sayHello("Bob") // выполняется конкурентно
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}Когда выполняется go sayHello("Alice"), она не ждет завершения sayHello. Вместо этого выполнение сразу переходит к следующей строке, пока sayHello работает в фоновом режиме. Вот почему нам нужен time.Sleep — без него main завершится до того, как горутины успеют завершить свою работу.
Вы также можете запускать горутины с помощью анонимных функций:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")Сама функция main выполняется в горутине. Когда она завершается, все остальные горутины немедленно прекращаются — у них нет возможности завершиться. Использование time.Sleep — это временное решение; в следующих уроках вы изучите правильные методы синхронизации с помощью каналов и групп ожидания.
Задание
ЛегкоДавайте создадим планировщик задач, который выполняет несколько задач одновременно, используя goroutines. Вы создадите простую систему, в которой задачи выполняются в фоновом режиме, пока основная программа продолжает работу, демонстрируя, как goroutines обеспечивают параллельное выполнение.
Вы организуете свой код в двух файлах:
tasks.go: Определите логику выполнения ваших задач.Создайте структуру
Taskс полямиName(string) иDuration(int, представляющее миллисекунды).Реализуйте метод
Run()дляTask, который имитирует работу, засыпая на время длительности задачи, а затем выводит:Task [Name] completedСоздайте функцию
RunAllTasks(tasks []Task), которая запускает методRun()каждой задачи как goroutine. После запуска всех goroutines она должна заснуть на 500 миллисекунд, чтобы дать им завершиться, а затем вывести:All tasks launchedmain.go: Считывает информацию о задачах и координирует параллельное выполнение.Считайте количество задач, затем считайте имя и длительность каждой задачи. Создайте задачи и передайте их в
RunAllTasks. После возврата функции выведите:Main function done
Будут предоставлены следующие входные данные:
- Строка 1: Количество задач (целое число)
- Следующие строки: Для каждой задачи две строки — имя задачи, затем её длительность в миллисекундах
Например, при вводе:
3
Download
100
Process
50
Upload
75Ваш вывод должен показывать завершение задач (порядок может меняться из-за параллелизма), за которым следуют сообщения о завершении:
Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function doneПримечание: Поскольку goroutines выполняются одновременно, задачи с меньшей длительностью обычно завершаются первыми. Порядок завершения задач зависит от их длительности — более короткие задачи завершаются раньше длинных.
Используйте time.Sleep с time.Duration и time.Millisecond для задержек. Помните, что без задержки в RunAllTasks функция вернет управление до того, как goroutines завершат свою работу.
Шпаргалка
Горутина (goroutine) — это легковесный поток выполнения, управляемый средой выполнения Go. Горутины запускаются с использованием всего нескольких килобайт стековой памяти, что позволяет запускать тысячи таких потоков одновременно.
Чтобы запустить горутину, поместите ключевое слово go перед вызовом функции:
func sayHello(name string) {
fmt.Println("Hello,", name)
}
func main() {
go sayHello("Alice") // выполняется конкурентно
go sayHello("Bob") // выполняется конкурентно
time.Sleep(100 * time.Millisecond)
fmt.Println("Done")
}Когда выполняется go sayHello("Alice"), программа не ждет завершения sayHello. Вместо этого она сразу переходит к следующей строке, пока sayHello выполняется в фоновом режиме.
Вы можете запускать горутины с помощью анонимных функций:
go func(msg string) {
fmt.Println(msg)
}("Hello from goroutine")Сама функция main выполняется в горутине. Когда она завершается, все остальные горутины немедленно прекращают работу. Используйте time.Sleep, чтобы дать горутинам завершиться до выхода из функции main.
Попробуйте сами
package main
import (
"fmt"
)
func main() {
// Считать количество задач
var numTasks int
fmt.Scanln(&numTasks)
// TODO: Создать слайс для хранения задач
// TODO: Считать название и длительность каждой задачи
// Для каждой задачи:
// - Считать название задачи
// - Считать длительность (как целое число)
// - Создать Task и добавить в ваш слайс
// TODO: Вызвать RunAllTasks с вашим слайсом задач
// TODO: Вывести "Main function done"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool