Язык Go — это язык для параллелизма.Язык Go — один из немногих языков, который реализует параллелизм на уровне языка; это также особенность параллелизма языка Go, которая привлекает бесчисленное количество разработчиков по всему миру.
Параллелизм и параллелизм
параллелизм: Две или более задачи выполняются за определенный период времени. Нас не должно заботить, выполняются ли эти задачи одновременно в определенный момент времени, могут ли они выполняться одновременно или нет, нас интересует только то, решает ли выполнение две задачи за период времени время, даже за короткий промежуток времени (одну секунду или две секунды) или две или более задач.
**Параллелизм: **Две или более задачи выполняются одновременно.
Параллелизм — это логическое понятие, в то время как параллелизм делает упор на физическом рабочем состоянии. Параллелизм «включает» параллелизм.
(См.: Роб ПайкPPT)
Модель параллелизма CSP в Go
Go реализует две формы параллелизма. Первый широко известен: многопоточная разделяемая память. По сути, это многопоточная разработка на таких языках, как Java или C++. Другой специфичен для Go и рекомендован Go: модель параллелизма CSP (общие последовательные процессы).
Модель параллелизма CSP — это концепция, предложенная примерно в 1970 г. Это относительно новая концепция.В отличие от традиционной многопоточной связи через общую память, CSP фокусируется на «совместном использовании памяти посредством связи».
Запомните следующую фразу:Do not communicate by sharing memory; instead, share memory by communicating.«Не общайтесь, делясь памятью, вместо этого делитесь памятью, общаясь».
Общие модели параллелизма потоков, такие как Java, C++ или Python, взаимодействуют между потоками через общую память. Очень типичным способом является доступ к общим данным (таким как массивы, Карты или структура или объект) через блокировки.Поэтому во многих случаях выводится удобная структура данных, называемая «поточно-безопасными структурами данных». Например, структуры данных в пакете «java.util.concurrent», предоставленном Java. Традиционная модель параллелизма потоков также реализована в Go.
Модель параллелизма CSP в Go реализована черезgoroutine
иchannel
быть реализованным.
-
goroutine
Это исполняющая единица параллелизма на языке Go. Это немного абстрактно, но похоже на традиционное понятие «нить», которое можно понимать как «нить». -
channel
каждая параллельная структура в языке Go (goroutine
) перед механизмом связи. С точки зрения непрофессионала, каждыйgoroutine
«Канал» для связи между ними чем-то похож на пайпы в Linux.
генерироватьgoroutine
Метод очень прост: иди, и он будет сгенерирован.
go f();
механизм связиchannel
Также очень удобно передавать данныеchannel <- data
, получить данные для<-channel
.
В процессе коммуникации данные передаютсяchannel <- data
и получить данные<-channel
Они должны появляться парами, потому что здесь проходят, там переходят, дваgoroutine
общение между ними.
И будет ли оно передано или взято, оно будет блокироваться до следующегоgoroutine
Проходи или бери.
есть дваgoroutine
, один из которых инициировалchannel
Операция передачи по значению была инициирована в . (goroutine
прямоугольник,channel
стрелка)
слеваgoroutine
Начните блокировать, ожидая, пока кто-нибудь его получит.
В это время правоgoroutine
Начата операция приема.
правая сторонаgoroutine
Он также начал блокироваться, ожидая, пока кто-то другой отправит его.
В это время обе стороныgoroutine
Оба нашли друг друга, поэтому двоеgoroutine
Начать пас, закрыть.
Это самая основная форма модели параллелизма Golang CSP.
Принцип реализации модели параллелизма Go
Начнем с потоков, какая бы модель параллелизма ни была на уровне языка, на уровне операционной системы она должна существовать в виде потоков. Операционная система может быть разделена на пространство пользователя и пространство ядра в соответствии с различными правами доступа к ресурсам.Пространство ядра в основном используется для доступа к аппаратным ресурсам, таким как ресурсы ЦП, ресурсы ввода-вывода и ресурсы памяти, и предоставляет самые основные базовые ресурсы для приложения верхнего уровня. Пространство пользователя — это фиксированное пространство активности приложения верхнего уровня. Пространство пользователя не может напрямую обращаться к ресурсам и должно вызывать ресурсы, предоставляемые пространством ядра, через «системные вызовы», «библиотечные функции» или «Шелл-скрипты».
Наш текущий компьютерный язык можно рассматривать как своего рода «программное обеспечение» в узком смысле.Так называемые «потоки» в них часто представляют собой потоки пользовательского режима, которые отличаются от потоков режима ядра самой операционной системы ( KSE для краткости).
Реализацию потоковой модели можно разделить на следующие способы:
Модель потоков на уровне пользователя
Как показано на рисунке, несколько потоков пользовательского режима соответствуют потоку ядра, и работа потока, такая как создание, завершение, переключение или синхронизация потока программы, должна выполняться сама по себе.
Модель потоков на уровне ядра
Эта модель напрямую вызывает поток ядра операционной системы, и все операции, такие как создание потока, его завершение, переключение и синхронизация, выполняются ядром. С++ как раз такой.
Двухуровневая модель потоков
Эта модель представляет собой модель потоковой передачи между моделью потоковой обработки на уровне пользователя и моделью потоковой обработки на уровне ядра. Реализация этой модели очень сложна.Как и в модели многопоточности на уровне ядра, процесс может соответствовать нескольким потокам на уровне ядра, но потоки в процессе не соответствуют один к одному с потоками ядра; эта модель многопоточности сначала создаст несколько потоков уровня ядра.Thread, а затем использует собственные потоки пользовательского уровня, чтобы соответствовать нескольким созданным потокам уровня ядра.Его собственные потоки пользовательского уровня должны быть запланированы их собственными программами, а ядро- потоки уровня передаются ядру операционной системы для планирования.
Модель потоков языка Go — это специальная двухуровневая модель потоков. Давайте пока назовем ее моделью "MPG".
Модель реализации потока Go MPG
M
ОтноситсяMachine
,ОдинM
Непосредственно связан с потоком ядра.P
Относится к «процессору», что означаетM
Требуемый контекст также является процессором, который обрабатывает логику кода пользовательского уровня.G
ОтноситсяGoroutine
, по сути, является облегченной нитью.
Отношения между тремя показаны на рисунке ниже:
На приведенном выше рисунке показан случай двух потоков (потоков ядра). M соответствует потоку ядра, а M также связан с контекстом P. Контекст P эквивалентен «процессору», а контекст связан с одной или несколькими горутинами. Число P(Processor) устанавливается равным значению переменной окружения GOMAXPROCS при запуске или при вызове функции во время выполненияruntime.GOMAXPROCS()
Сделайте настройки. Фиксированное количество процессоров означает, что только фиксированное количество потоков выполняет код go в любой момент времени. Горутина — это код, который мы хотим выполнять одновременно. На рисунке P выполняетGoroutine
синий; в ожидании исполненияGoroutine
серый, серыйGoroutine
образовали очередьrunqueues
Макрокартина отношений между этими тремя выглядит следующим образом:
Отказаться от P (процессор)
Вы можете задаться вопросом, зачем нужен контекст, можем ли мы просто удалить контекст и позволитьGoroutine
изrunqueues
А как насчет повесить на М? Ответ — нет, цель контекста — позволить нам освобождать другие потоки напрямую, когда поток ядра заблокирован.
Очень простой пример — системный вызовsysall
, поток не должен выполнять код одновременно, а системный вызов заблокирован.В это время поток M должен отказаться от текущего контекста P, чтобы другиеGoroutine
Запланировано выполнение.
Как показано на левой картинке выше, G0 в M0 выполняет системный вызов, а затем создает M1 (он также может существовать, но не создан) (повернуться к правому рисунку), а затем M0 отбрасывает P и ждет возвращаемого значения системного вызова. , M1 Accepted P, продолжит выполнениеGoroutine
другое в очередиGoroutine
.
Когда системный вызов завершается, M0 "украдет" контекст. Если это не удастся, M0 поместит свою Gouroutine G0 в глобальную очередь выполнения, а затем поместит ее в пул потоков или перейдет в спящий режим. Глобальная очередь выполнения — это место, которое каждый P использует для извлечения новых горутин после запуска собственной локальной очереди выполнения горутин. P также будет периодически проверять горутины в глобальной очереди выполнения, в противном случае горутины в глобальной очереди выполнения могут не выполняться и умереть от голода.
Сбалансированное распределение работы
Согласно приведенному выше утверждению, контекст P будет периодически проверять горутины в глобальной очереди горутин, чтобы ему было чем заняться при использовании своей собственной очереди горутин. Что делать, если горутины в глобальной очереди горутин исчезли? Просто украдите его из очереди других запущенных Ps.
в каждом пGoroutine
Также различны результаты их эффективности и времени работы.В среде со многими P и M один P не может работать самостоятельно.Goroutine
Делать нечего, потому что, может быть, другие Ps очень долгоgoroutine
Чтобы очередь работала, она должна быть сбалансирована.
Как это решить?
Подход Go также прост: он крадет половину других P!
использованная литература:The Go scheduler"Go Concurrent Programming First Edition"