Hello, Go
В этом уроке вы узнаете:
- историю возникновения Go;
- особенности языка;
- задачи и проблемы, которые решает Go.
Если вы проходили курс «Основы Go», можете пролистать эту тему и сразу перейти к Спринту 1. Начнём с главного: откуда взялся голубой суслик? По-английски суслик — gopher. Автор оригинального изображения — иллюстратор и писатель
Рене Френч, жена Роба Пайка, одного из создателей Go. Изначально она рисовала суслика для промоушена радиостанции WFMU в Нью-Джерси. Спустя несколько лет, когда стартовал проект Go и понадобился маскот, Рене адаптировала рисунок гофера. Так суслик стал талисманом нового языка программирования.
Изначально гофер был светло-коричневого цвета. Но когда решили выпустить мягкие игрушки, первые плюшевые экземпляры покрасили в синий. Так и повелось. Подробнее про гофера можно почитать
в официальном блоге Go.
Важно знать, что правильное название языка — Go, а не Golang. Второй вариант используют потому, что go — часто употребляемый английский глагол, это затрудняет поиск в интернете материалов, относящихся к языку программирования. Из тех же соображений выбирали домен для официального сайта:
golang.org. Недавно его заменили на
go.dev.
Зачем миру понадобился ещё один язык
Несмотря на то что Go — сравнительно молодой язык (первая версия появилась в 2009 году, а в 2012-м состоялся первый стабильный релиз), потребность в подобном языке возникла на рынке задолго до его создания.
Требования бизнеса к программному продукту начали меняться с появлением интернета. Нужно было создавать веб-страницы быстро, не жертвуя при этом безопасностью. Серверы должны были обслуживать нескольких клиентов одновременно — чем больше, тем лучше. А железо, которое обеспечивало работу большинства программных продуктов того времени, по-прежнему работало с одноядерными процессорами.
С приходом на рынок многоядерных процессоров появилось дополнительное пространство для оптимизации. Новые условия требовали новых подходов к проектированию ПО. В частности, оно должно было эффективно утилизировать доступные ядра процессора. Для этого программистам надо было писать код, который поддерживал многопоточность.
Совсем чуть-чуть теории
Что такое «поток» в контексте операционной системы? Поток выполнения (native/kernel thread) — это часть процесса, в которой инструкции могут выполняться независимо и иметь доступ к общим ресурсам. За управление потоками отвечает планировщик ОС.
Многопоточность — свойство железа и софта, при котором несколько потоков могут выполняться параллельно, не мешая друг другу. Если разработчики ПО сумели эффективно распараллелить отдельные части программы, можно рассчитывать на увеличение производительности, кратное количеству доступных ядер процессора.
Одна из первых вещей, с которой сталкиваются разработчики при проектировании многопоточного ПО, — организация доступа к общим ресурсам, а конкретно — к памяти. Неверное разделение доступов между потоками может привести к порче данных. Например, если два потока одновременно и параллельно пишут своё значение в одну и ту же переменную, какое из значений будет записано в результате? Да и в целом необходимость мыслить несколькими потоками осложняет понимание процесса выполнения программы.
Разработчикам хочется избавиться средствами языка от головной боли, которая может возникнуть при переходе на многопоточное программирование. Разные языки предлагали разные решения этой проблемы, но так как большинство существовавших на тот момент языков создавались без учёта многопоточности, её поддержку приделывали «сбоку».
В интерпретируемых языках программирования, занимавших львиную долю рынка того времени, были дополнительные сложности. Нужно было организовать механизмы обращения к глобальному состоянию интерпретатора из нескольких потоков и обмен данными между этими потоками.
Появилось понимание, что необходим новый язык программирования, который был бы изначально создан с расчётом на многопоточность и позволял быстро и удобно писать приложения, утилизирующие доступные ядра процессоров.
Как Go ответил на вызовы рынка
Возможность спроектировать язык с нуля позволила на фундаментальном уровне внедрить в язык многие решения, которые позволили бы разработчикам писать многопоточный код.
В язык встроено две абстракции, которые реализуют модель CSP (Communicating Sequential Processes). В этой модели программа представляет собой множество одновременно работающих подзадач, которые общаются между собой с помощью каналов связи.
В Go модель CSP реализована следующими абстракциями:
- Подзадачей в Go именуется goroutine (горутина). Горутины живут в прослойке между программой и средой выполнения, именуемой runtime. Помимо всего прочего, рантайм отвечает за организацию конкурентного доступа множества горутин к общему ограниченному ресурсу — процессорному времени. Его задача — распределить этот ресурс так, чтобы каждая неспящая горутина смогла поработать хотя бы какое-то время, прежде чем управление перейдёт к следующей. Таким образом достигается иллюзия параллельности выполнения задач при количестве горутин, многократно превышающем количество доступных системных потоков.
- Каналом связи в Go выступает такая абстракция, как channel (канал). На каналах построены все механизмы обмена и синхронизации потоков в Go. Канал представляет из себя этакий туннель, в который одна горутина может «положить» значение, а другая — это значение оттуда «вытащить» и что-то с ним сделать.
Ещё раз:
Обе абстракции — не подключаемые библиотеки или фреймворки, а встроенные конструкции синтаксиса, что делает работу с ними «киллер-фичей» для языка и большим преимуществом для программиста.