-
Notifications
You must be signed in to change notification settings - Fork 17
Синтаксис языка
Синтаксис языка основывается на литералах, используемых в платформе 1С:Предприятие 8, что значительно облегчает изучение языка для программистов 1С. Дополнительно к этому, используются литералы массивов и структур javascript/json и срезы массивов/строк из python, чего так не хватало в 1С.
Файлы со скриптами на языке "Гонец"
Подгрузка кода из других файлов
Блоки кода и области видимости
Регистр букв в идентификаторах и ключевых словах
Системные функциональные структуры (объекты метаданных)
Объявление переменных и присвоение им значений
Прерывание цикла или блока кода
Приоритет операторов в выражениях
Ключевые слова, аналогичные таким же из языка 1С:Предприятие
-
Функция, КонецФункции - определяют функции (процедур нет, процедурой будет любая функция без возвращаемого значения). Функции могут быть анонимными или именованными, присваиваться переменным, передаваться как параметр в другие функции.
-
Возврат - возвращает значение из функции (или несколько, через запятую)
-
Если, Тогда, ИначеЕсли, Иначе, КонецЕсли - определяют условную конструкцию
-
Для по Цикл, Для каждого из Цикл, Пока Цикл, КонецЦикла - определяют циклы
-
Прервать, Продолжить - выход из цикла или его продолжение с первой строки
-
Попытка, Исключение, КонецПопытки - определяют блок обработки возникающих ошибок
-
ВызватьИсключение - инициирует ошибку для блока обработки
-
Новый - для создания сложных переменных-объектов, массивов и каналов
-
Истина, Ложь, И, ИЛИ, НЕ - для логических выражений
-
Неопределено, NULL - для пустых значений
Новые ключевые слова, расширяющие синтаксис
-
Импорт - импортирует модули стандартной библиотеки
-
Модуль - для определения названия модуля (для использования в других модулях, включая глобальный контекст)
-
Выбор, Когда, Другое, КонецВыбора - выбор исполнения блоков кода по условию, аналог switch в golang (упрощает сложные конструкции "ИначеЕсли"), также требуется при работе с каналами и горутинами
-
Старт - запускает функцию в горутине (отдельном потоке исполнения), аналог go в golang
-
Канал - определяет сложный тип "канал", который используется для взаимодействия между параллельно выполняющимися функциями, запущенными с помощью "Старт", аналог chan в golang
Каждый файл со скриптами может состоять из нескольких областей комментариев и кода, может содержать код, относящийся к разным модулям. Модуль может состоять из частей, размещенных в разных файлах, эти части исполняются (загружаются в окружение) в порядке загрузки файлов на исполнение командой ЗагрузитьИВыполнить
в коде на языке Гонец.
Расширение файла со скриптом (для различных утилит): ".gnc". Выполнение файла со скриптом производится командой:
gonec.exe test.gnc
Расширение файла с компилированным бинарным кодом для виртуальной машины: ".gnx". Компиляция файла с исходным кодом "test.gnc" в файл "test.gnx" производится командой:
gonec.exe -c test.gnc
При компиляции, суффикс "gnx" автоматически добавится к имени исходного файла (вместо ".gnc", если он был), и под этим именем будет сохранен новый файл.
Выполнение скомпилированного файла производится командой (интерпретатор определяет скомпилированный файл по расширению ".gnx"):
gonec.exe test.gnx
Обычно подгрузка нужна для того, чтобы подгрузить модули из других файлов со скриптами языка gonec. Эти модули могут содержать библиотечные функции, которые могут разрабатывать другие разработчики. Таким образом, осуществляется раздельный доступ к редактированию кода.
Подгрузка и исполнение кода из других файлов на языке Гонец осуществляется командой ЗагрузитьИВыполнить("путь_и_имя_файла")
. Путь и имя файла должно содержать прямые (а не обратные) слэши для windows (а для linux это всегда так и было). Загрузка и исполнение скомпилированных файлов .gnx так же поддерживается.
Загрузка файла может возвращать значение, если в загружаемом файле в глобальном контексте выполняется команда Возврат
. Это значение можно присваивать переменным и работать с ним как с обычным возвращаемым из функции значением.
Пример:
ЗагрузитьИВыполнить("D:/golang/src/github.com/covrom/gonec/test.gnx")
Комментарии обозначаются двумя способами: #комментарий
и //комментарий
.
Комментарий начинается с соответствующего символа и распространяется до конца строки.
Многострочные комментарии не предусмотрены (как и в 1с).
Блоки кода являются областями видимости переменных. Есть глобальная область видимости, в которую входят модули на языке Гонец, структуры и функции ядра стандартной библиотеки (на Го). Переменные и код, введенные без каких либо рамок и исполняемые при запуске файла в интерпретаторе, относятся к глобальному модулю "_". У каждой функции, в том числе, анонимной, есть своя индивидуальная область видимости, дополнительно к глобальной. Только анонимные функции могут видеть область видимости породившей ее функции. Именованные функции, вызванные внутри других функций, не имеют доступа к области видимости родительской функции.
Пример:
#!gonec -комментарий, который можно использовать для определения того, что этот скрипт можно запустить интерпретатором gonec в командной строке linux
//модуль глобального контекста
Модуль МойМодуль
Функция Фун1()
Сообщить("МойМодуль.фун1")
Возврат 1
КонецФункции
//Модуль _ - это глобальный контекст
Модуль _
//блок кода глобального контекста, он будет выполняться при запуске скрипта в интерпретаторе
абв = МойМодуль.Фун1()
Сообщить(абв)
СообщитьФ("Форматированная строка: %v, hex=%X\n",123,123)
Результат:
МойМодуль.фун1
1
Форматированная строка: 123, hex=313233
Все идентификаторы в языке Гонец регистронезависимы, т.е. заглавные и строчные буквы - равнозначны (как в 1с). Исключением является обращение к полю структуры языка Гонец, имя которого было ранее определено строкой (т.е. если к нему определено обращение по индексу имя_структуры["имя поля"]
).
Поддерживается разделение операторов через ';'
, как в 1с, но оно необязательно - интерпретатор прекрасно понимает синтаксические конструкции и без точки с запятой. Например:
а = Неопределено
Сообщить(а)
а = истина
Сообщить(а)
а = ложь
Сообщить(а)
б = (2 = 3)
в = (3 = 3)
сообщитьф("%v %v\n",б,в)
сообщить(а=истина)
сообщить(а=ложь)
Строки в языке Гонец определяются так же, как и в golang. Строки могут быть заключены в двойные или одинарные кавычки. Внутри строк можно указывать экранируемые спецсимволы из следующего перечня:
\a U+0007 сигнал
\b U+0008 backspace
\f U+000C form feed
\n U+000A line feed или newline (новая строка)
\r U+000D carriage return (возврат каретки)
\t U+0009 табуляция
\v U+000b вертикальная табуляция
\\ U+005c backslash (сам символ \)
\' U+0027 одиночная одинарная кавычка
\" U+0022 одиночная двойная кавычка
\u12e4 - символ Unicode с шестнадцатеричным кодом
Для строк есть стандартные функции, см. в соответствующем разделе Работа со строковыми значениями.
Числовые литералы определяются как и в 1С, т.е. набором цифр, разделенных точкой (для чисел с плавающей запятой), или без точки (для целых чисел).
Так же можно (в отличие от 1С) указывать 16-ричные значения с префиксом 0x, например, 0xFFFF = 16535, 0xFF = 255.
Числа в экспоненциальной записи (степени 10) указываются через символ 'e' (английская), за которым следует знак порядка. Например, 1e+10.
Числа с плавающей запятой Гонец хранит и обрабатывает в формате decimal128 разрядностью 128 бит, с помощью библиотеки gcc libdecnum, написанной на языке C. Такой формат позволяет сохранять абсолютную точность при вычислениях с плавающей запятой. Гонец избавлен от проблем, которые возникают в других языках и Excel при использовании обычных форматов значений с плавающей запятой: https://habrahabr.ru/post/309812/
Округление чисел с плавающей запятой по правилам арифметики, принятой в России, осуществляется с помощью функции Окр(число знаков)
, где число знаков > 0 означает округление до этого количества цифр после запятой, а число знаков < 0 означает округление на это число знаков до запятой.
Литералы даты, в отличие от 1С, записываются в виде обычных символьных строк в формате RFC3339.
"2006-01-02T15:04:05+03:00" = 2 января 2006 года 15 часов 4 минуты 5 секунд, Московское время
Преобразование строк в даты осуществляется функцией Дата(переменная_или_строковый_литерал)
Массивы и структуры определяются соответствующими встроенными типами Массив
и Структура
.
Реализация приближена к стандарту json, благодаря чему, эти типы очень удобно использовать для работы с вэб и с базами данных.
В коде массивы и структуры создаются с помощью литералов [элемент1, элемент2, [вложенный массив]]
для массивов и {"ключ1":значение1, "ключ2":{вложенная структура}}
для структур.
Опционально можно указывать размер массива в скобках после его определения без содержимого: [](длина)
. В этом случае, будет создано указанное количество элементов массива со значением "Неопределено". Можно так же указать резерв под добавляемые элементы, это будет ускорять операции объединения массивов и добавления в них новых значений:[](длина, резерв)
.
Так же возможно создание массивов с помощью конструкции Новый Массив
(равнозначно литералу []
), а структур - Новый Структура
(равнозначно литералу {}
) - в этом случае создаются пустые массивы и структуры.
Если содержимое литералов массивов или структур переносится на следующие строки, то в конце каждой строки обязательна запятая, в том числе, в конце последней строки, например:
ст = {"А":1,
"Б":2,
}
Ключи структур могут быть только строковыми значениями, но наличие в них тех или иных символов не регламентировано, т.е. могут быть и пробелы, и символы из языков народов мира. Структуры работают быстро (реализованы на map-типе golang). Ключи зависят от регистра букв.
Если ключи структур похожи на имена переменных, то их можно разыменовывать через точку, например:
а={"Первая":{"Вторая":123}}
сообщить(а.Первая.Вторая)
Массивы и структуры могут использоваться в качестве типов для передачи в горутины и каналы.
Пример использования массивов и структур в каналах:
а=[1,2,3,4,5]
б={"а":1,
"б":2,
}
сообщить(а,типзнч(а))
сообщить(б,типзнч(б))
функция вканал(кан,а)
#отправляем значение в канал
кан <- а
конецфункции
кан = новый канал(0)
# стартуем параллельно исполняемую горутину, передаем в нее канал, по которому будем с ней общаться
старт вканал(кан,а)
# ожидаем, когда горутина положит в канал данные, и считаем их в переменную
в = <- кан
сообщить(в, типзнч(в))
Указанный выше код выведет результат:
[1,2,3,4,5] массив
{"а":1,"б":2} структура
[1,2,3,4,5] массив
У структур есть встроенные функции значение_структура.Ключи()
и значение_структура.Значения()
- они возвращают массивы ключей или значений соответственно. Ключи в массиве ключей будут отсортированы по возрастанию. А вот порядок значений в массиве значений будет неопределен.
Массивы можно получать из высокоэффективного, синхронизированного между горутинами, пула повторного использования памяти (sync.Pool из Го), и возвращать их обратно.
Для этого предусмотрены функции массив=ПолучитьМассивИзПула()
и ВернутьМассивВПул(массив)
. Массивы всегда получаются из пула пустыми, однако, место в памяти, которое они будут занимать при добавлении элементов, будет использоваться повторно с момента последнего аналогичного использования массива, возвращенного в пул.
Использование пула существенно сокращает использование памяти при работе с большим количеством временно создаваемых массивов.
Каналы аналогичны по функциональности каналам в Го, и служат для передачи значений между параллельно выполняющимися горутинами, запущенными директивами Старт имя_функции(парамтеры)
или Старт Функция(параметры_анонимной_функции) ...
.
Каналы создаются директивой Новый Канал(0)
для небуферизированных каналов, или Новый Канал(размер_буфера)
для буферизированных.
Приведение типов в языке Гонец сделано максимально удобно для работы с коллекциями и json. Пример:
# массив с вложенным массивом со структурой и датой
а=[1, 2, [3, {"а":1}, Дата("2017-08-17T09:23:00+03:00")], 4]
# приведение массива или структуры к типу "строка" означает сериализацию в json, со всеми вложенными массивами и структурами
Сообщить(а)
Сообщить(Строка(а))
# приведение строки к массиву или структуре означает десериализацию из json
Сообщить(Массив("[1,2,3,4]"))
Сообщить(Массив(Строка(а)))
Получаем результат:
[1,2,[3,{"а":1},"2017-08-17T09:23:00+03:00"],4]
[1,2,[3,{"а":1},"2017-08-17T09:23:00+03:00"],4]
[1,2,3,4]
[1,2,[3,{"а":1},"2017-08-17T09:23:00+03:00"],4]
Простые типы "строка", "число", "булево", "целоечисло" - приводятся друг в друга почти так же, как и для других языков программирования, включая 1С. Числа с плавающей запятой должны содержать "." как разделитель целой и дробной части.
Строка конвертируется в булево "истина" из следующего набора строк: истина
,true
в любом регистре, в остальных случаях получается "ложь".
Булево в строку всегда превращается в один из вариантов истина
или ложь
.
Дата конвертируется в строку в формате RFC3339. Верно и обратное, строка в том же формате RFC3339 конвертируется в дату с помощью функции Дата(строка)
. Данный формат поддерживается json, а так же он распространен при программировании под мобильные платформы, именно поэтому был выбран именно он, а не стандарт даты 1С.
Разность между датами определяется в Гонце типом Длительность
. Любое выражение с разностью дат порождает значение такого типа.
Длительность конвертируется в Число или ЦелоеЧисло и обратно. Для целого числа, конвертация производится в количество секунд. Для числа с плавающей запятой, точность длительности после запятой доходит до наносекунды (множитель 1e9). Обратная конвертация, из числа секунд (ЦелоеЧисло) или секунд с точностью до наносекунды после запятой (Число) в длительность производится при передаче числа в аргумент функции Длительность(число_или_целоечисло)
а=Дата("2017-08-17T09:23:00+03:00")
СообщитьФ("%#v\n",а)
б=Дата(а+24*60*60)
в=Дата(а+Длительность(24*60*60))
Сообщить(Строка(б), в)
Результат:
core.VMTime{wall:0x0, ext:63638547780, loc:(*time.Location)(0xe90580)}
"2017-08-18T09:23:00+03:00" 2017-08-18 09:23:00 +0300 MSK
Булево в число конвертируется так: для "истина" в 1 (целоечисло) или 1.0 (число), а для "ложь" в 0.
Число в булево конвертируется для всех значений >0 в "истина", а для <=0 - в "ложь".
Структуры языка (Структура
) конвертируются в системные функциональные структуры (объекты метаданных), через соответствующее указание литералов типов, объявленных в системных библиотеках Новый ("Системный_функциональный_тип", структура)
. Обратно можно получить структуру через сериализацию ее в JSON функцией Структура(Строка(объект метаданных))
Системные функциональные структуры сериализуются в json при использовании приведения к типу Строка(объект_функциональной_системной_структуры)
и обратно при использовании `Новый("Системный_функциональный_тип", строка).
В системных библиотеках языка могут создаваться объекты метаданных, при импорте соответствующей библиотеки (см. Импорт
выше). Данные объекты метаданных являются функциональными структурными типами, функциональность которых скомпилирована на языке Го. Объекты таких метаданных имеют встроенные методы для работы с ними.
Создать новый функциональный структурный тип средствами языка Гонец нельзя, по соображениям производительности. В случае необходимости разработки нового типа объектов метаданных, необходимо реализовывать их на языке Го и компилировать новую версию интерпретатора. Поскольку интерпретатор представляет из себя standalone-решение из одного исполняемого файла без внешних зависимостей, то такой подход к разработке метаданных является наилучшим и самым быстрым. Компиляция усовершенствованной версии интерпретатора выполняется одной командой go build .
в папке с исходными текстами, и занимает всего несколько секунд.
Структуры языка (Структура
) конвертируются в системные функциональные структуры (объекты метаданных типа struct
языка Го), а так же сериализуются в json и обратно (см. Приведение типов).
Можно создавать переменные библиотечных типов с помощью конструкций:
-
Новый(имя_структурного_функционального_типа_строкой)
- создает новую переменную такого типа -
Новый(имя_структурного_функционального_типа_строкой, источник_заполнения)
- создает новую переменную такого типа и заполняет ее из json представления, если источник типа "строка", или заполняет значениями структуры, если тип "структура".
Пример:
# Создадим функциональную структуру с заполнением из обычной структуры:
а = Новый("__ФункциональнаяСтруктураТест__",{"ПолеЦелоеЧисло":5,"ПолеСтрока":"srtg"})
Сообщить(а)
Сообщить(Строка(а))
Сообщить(Структура(Строка(а)))
а.ПолеСтрока = "edrgwerg"
Сообщить(а.ВСтроку()) // обратите внимание - это метод системной функциональной структуры, определен средствами языка Го
# теперь заполним из json, который получим через конвертацию структуры в json, приведением ее в тип строки:
б = Строка(а)
а = Новый("__ФункциональнаяСтруктураТест__", б)
Сообщить(а)
Выведет результат:
{"ПолеЦелоеЧисло":5,"ПолеСтрока":"srtg"}
{"ПолеЦелоеЧисло":5,"ПолеСтрока":"srtg"}
{"ПолеСтрока":"srtg","ПолеЦелоеЧисло":5}
ПолеЦелоеЧисло=5, ПолеСтрока=edrgwerg
{"ПолеЦелоеЧисло":5,"ПолеСтрока":"edrgwerg"}
Переменные могут быть объявлены в области видимости с помощью присвоения переменной значения НоваяПеременная = 123.45
, как в 1С.
Возможно объявление и присваивание сразу нескольким переменным в одной инструкции:
а,б,в = 1,2,3
г,д = а+1*б,в-2
сообщить(г,д)
ж[7], з[15] = 0, 5
Можно присваивать значения элементам коллекций (например, массивов, структур), при этом, коллекция уже должна содержаться в переменной в левой части выражения (см. выше ж[7]
- в переменной ж
уже должен содержаться массив).
Модули нужны для организации библиотек функций. Объявление модуля производится конструкцией Модуль, и начиная со следующей строки и до конца текста программы, либо до следующей конструкции Модуль, будет располагаться текст модуля. Такая организация позволяет разбивать тексты модулей на разные файлы и наоборот, в одном файле запрограммировать несколько модулей.
Модуль ИмяМодуля1
#Текст модуля, включает объявления функций
//Модуль имеет собственное глобальное состояние, т.е. можно включать в него код, не обрамленный функциями, и можно объявлять переменные модуля
Модуль ИмяМодуля2
// а это уже другой модуль
Код модуля исполняется в тот момент, когда интерпретатор встречает объявление модуля, например, в момент загрузки и выполнения файла с этим модулем. Пример использования библиотечной функции модуля:
Модуль МойМодуль
Функция Фун1()
Сообщить("МойМодуль.фун1")
Возврат 1
КонецФункции
Модуль ТвойМодуль
абв=1
Модуль МойМодуль
абв=134
Модуль _
абв = МойМодуль.Фун1() + 5
Сообщить(абв, МойМодуль.абв, ТвойМодуль.абв)
Предопределенный модуль с именем "_" - это глобальный модуль. Все операторы такого модуля исполняются в глобальном контексте.
Содержимое текущей области видимости (например, модуля) можно вывести с помощью отладочной функции __Дамп__()
.
Из функций может быть произведен возврат одного (как в 1С) или нескольких (как в golang) значений. Для возврата нескольких значений используется выражение:
Функция ТрехкратныйВозврат()
Возврат 10.5, 2, 345
КонецФункции
п1 = ТрехкратныйВозврат()
Сообщить(п1) // будет выведен массив
п1, п2, п3 = ТрехкратныйВозврат()
Сообщить(п1, п2, п3) // будет выведено три переменные, в каждой отдельное число
В приведенном примере делается возврат трех значений из функции и присваивание их трем переменным. Множественный возврат, как правило, используется для "тихой" работы с ошибками (без вызова исключений).
Вызов исключения производится так же, как и в 1С, с помощью инструкции ВызватьИсключение "Описание ошибки"
. Содержимое ошибки можно получить внутри блока Исключение
с помощью функции ОписаниеОшибки()
Пример:
Попытка
Попытка
ВызватьИсключение "ащо"
Исключение
ВызватьИсключение "ыукшар"
Конецпопытки
Исключение
Сообщить("Обработана ошибка " + ОписаниеОшибки())
КонецПопытки
Цикл обхода коллекций (как в 1С) и считывания значений из канала (как в Го) определяется следующей конструкцией:
Для каждого Элемент Из ПеременнаяКоллекцияИлиКанал Цикл
#тело цикла, область видимости которого совпадает с областью видимости, которая содержит цикл снаружи
КонецЦикла
При обходе структур можно использовать встроенные функции для этих значений значение_структура.Ключи()
и значение_структура.Значения()
.
Доступна функция-генератор массива по диапазону чисел, которую можно обходить в цикле: Диапазон(начальное целое число, конечное целое число)
- получит массив, включая границы.
Если не указать начальное целое число, то оно будет принято равным 0.
Обычный цикл, аналогичен циклу 1С:
Для н=1 По 1000 Цикл
#тело цикла
КонецЦикла
В отличие от 1С, такой цикл в интерпретаторе Гонец работает стабильно, т.е. значение итератора устанавливается перед очередной итерацией в порядке очереди и вне зависимости от того, что ему было присвоено внутри цикла. Выражение для начального и конечного значения вычисляются перед началом цикла, и в ходе работы тела цикла повторно уже не вычисляются, что позволяет изменять внутри цикла любые переменные, входящие в выражения для начального и конечного значения, не беспокоясь о том, что цикл станет непредсказуемым. Так же, интерпретатор распознает восходящий и нисходящий перебор, и выбирает соответствующее направление приращения итерации автоматически.
Пример успешно выполняющегося цикла с учетом сказанного:
Нач, Кон = 10, -5
Для н = Нач По Кон Цикл
Сообщить(н)
н = 5
Сообщить(н)
Нач = Кон *100
Кон = 0
КонецЦикла
Цикл аналогичен такому же циклу в 1С. Выполняется, пока истинно выражение:
н=1
Пока н<10 Цикл
н++
Сообщить(н)
КонецЦикла
В языке Гонец можно прервать исполнение любого цикла командой Прервать
, как в 1С.
Для каждого н из [1,2,3] Цикл
Сообщить(н)
Если н=2 тогда
Прервать
КонецЕсли
КонецЦикла
Сообщить("ок")
Так же как и в 1С, в языке Гонец есть продолжение цикла (переход на следующую итерацию) с помощью инструкции Продолжить
.
Условные конструкции полностью соответствуют синтаксису 1С. Выражения, возвращающие значения "истина" или "ложь" имеют расширенный синтаксис, который поддерживает как операции сравнения 1С, так и операции сравнения golang. Например:
а, б = 0, 5
Если (а<>0 или б!=0) && (а<>7 || а=б) Тогда
Сообщить(1)
ИначеЕсли а>=б или б=0 Тогда
Сообщить(2)
Иначе
Сообщить(3)
КонецЕсли
Тернарный условный оператор реализуется аналогично 1С, т.е. в формате ?(Условие, ЗначениеДляИстина, ЗначениеДляЛожь)
.
Поддерживаются следующие операторы, в порядке приоритета (первые рассчитываются раньше, находящиеся в одной строке - равнозначны):
1. -(унарный минус), !(не), НЕ, ^(бинарное исключающее или), &(адрес), *(извлечение из адреса)
2. *(умножение), /(деление), %(остаток от деления)
3. +(сложение), -(вычитание), ++(увеличение на 1), --(уменьшение на 1)
4. ">"(больше), >=, <(меньше), <=, <<(побитный сдвиг влево), >>(побитный сдвиг вправо)
5. =, ==(равно), !=, <> (неравно), ","(запятая - для перечислений значений и переменных)
6. Идентификатор (с точками)
7. &&, И (булево И)
8. ||, ИЛИ (булево ИЛИ)
9. ?(тернарный оператор), ":"(разделитель в структурах)
10. =(присваивание)
Блок обработки исключений, возникших при выполнении блока кода, может перехватывать и обрабатывать исключения. Синтаксис аналогичен 1С:
Попытка
ВызватьИсключение "Ошибка!"
Исключение
Сообщить(ОписаниеОшибки())
ВызватьИсключение ОписаниеОшибки
КонецПопытки
Внутри блока Исключение
доступна функция ОписаниеОшибки()
, которая возвращает текст возникшей ошибки.
Конструкция "Выбор" является новой для специалистов, программирующих на 1С. Она позволяет выбирать из вариантов соответствующую ветку исполнения кода.
а=1
Выбор а:
Когда 0:
Сообщить(0)
Когда 1:
Сообщить(1)
Другое:
Сообщить("не найдено")
КонецВыбора
Для обычных значений этот оператор работает аналогично оператору если-иначеесли. Для каналов (когда не указано выражение выбора) - выбирает готовое значение из случайно выбранного канала в ветках "когда", либо переходит в ветку "другое".
Многопоточное программирование на языке Гонец с использованием каналов (при общении потоков-горутин между собой) предполагает возможность ожидания выполнения первой успешной операции с каналами из определенного перечня. Для этих целей служит конструкция выбора из операций с каналами. Такой выбор характеризуется отсутствием аргумента у конструкции Выбор:
Функция ПослатьВКанал(к)
к <- "Привет!"
КонецФункции
канал1, канал2 = Новый Канал(0), Новый Канал(0)
Старт ПослатьВКанал(канал2)
Старт ПослатьВКанал(канал1)
а=""
Выбор:
// переменная "а" должна быть заранее определена, иначе здесь будет ошибка интерпретации
Когда а <- канал1:
Сообщить(1)
Сообщить(а)
Когда а <- канал2:
Сообщить(2)
Сообщить(а)
КонецВыбора
В каждом выражении "Когда" можно либо получать из канала, либо отправлять значение в канал (см. работу с каналами). Результат получения из канала можно присваивать переменным, при этом, они должны быть определены до начала конструкции "Выбор".
Если отсутствует секция "Другое", то выбор бесконечно ожидает выполнения одной из операций с каналами, пока какая-либо из них не сработает успешно, т.е. в канале появится значение (для получения) или канал примет значение (для отправки), или пока какой либо из каналов не будет закрыт в другом потоке-горутине.
Если есть секция "Другое", то она выполняется в том случае, если ни одна из операций с каналами не выполнилась (т.е. вместо бесконечного ожидания выполняется секция "Другое"). Пример:
канал1, канал2 = Новый Канал(0), Новый Канал(0)
// сейчас каналы пустые, и будут ожидать отправки в них, а чтение из каналов будет бесконечно ожидать появления в них первого значения
а=""
Выбор:
// переменная "а" должна быть заранее определена, иначе здесь будет ошибка интерпретации
Когда а <- канал1:
// мы не запускали горутины с записью в канал1, поэтому, эта ветка будет бесконечно ожидать
Сообщить(1)
Сообщить(а)
Когда а <- канал2:
// мы не запускали горутины с записью в канал2, поэтому, эта ветка будет бесконечно ожидать
Сообщить(2)
Сообщить(а)
Другое:
Сообщить("Идем дальше!")
КонецВыбора
Под горутинами в языке Гонец понимаются функции, вызванные с помощью ключевых слов Старт функция(параметры)
или Параллельно функция(параметры)
.
Вызванные таким образом функции выполняются в отдельных контекстах, конкурентно с основным потоком программы. Конкурентность реализована полностью на механизме горутин языка Го.
Из одной горутины (основной поток программы тоже является горутиной) можно приостановить выполнение для того, чтобы другие горутины получили квант времени для своей работы. Это делается с помощью вызова функции ОбработатьГорутины()
.
Взаимодействие между горутинами возможно через каналы, а так же с помощью системного объекта с типом ГруппаОжидания
.
Группа ожидания предназначена для приостановки потока выполнения до того момента, пока горутины в группе ожидания не закончат свою работу (и оповестят группу ожидания об этом).
Группы ожидания реализованы на базе sync.WaitGroup из языка Го.
Создание группы ожидания производится так: гр = Новый ГруппаОжидания
.
Группа ожидания должна быть передана во все горутины (при старте или через канал), которые должны входить в нее.
Группа ожидания имеет методы:
-
Добавить(число горутин)
- добавляет число горутин, которые должны сообщить о завершении -
Завершить()
- горутина с помощью этого метода сообщает о завершении своей работы -
Ожидать()
- горутина, в которой будет вызван этот метод, приостановится до того момента, пока число вызовов Завершить() в других горутинах не сравняется с числом, добавленным вызовами Добавить(н).