Структура программы

Конкурентность

Именованные задачи с ограничением времени: успех, тайм-аут, значения Result и ошибки выполнения.

Используйте concurrent, когда независимые выражения могут выполняться вместе, а вызывающему коду нужна одна запись с именованными результатами. Для обычной прикладной программы выбирайте ограниченную форму с явным запасным значением на случай тайм-аута.

Успеваем до истечения времени

Сохраните следующий код как concurrency-success.tpz:

TOPAZ
let result = concurrent(timeout: 3s) {
    total: 1 + 2
    label: "ready"
} else {
    { total: -1, label: "timeout" }
}

print("{result.total} {result.label}")

Проверьте и запустите его:

BASH
topaz check concurrency-success.tpz
topaz run concurrency-success.tpz

Обе задачи заканчиваются до предельного срока, поэтому их имена становятся полями результата:

Вывод
3 ready

Запись связывает каждое значение с именем задачи. Она не сообщает, какая задача началась или завершилась раньше.

Выбираем запасную ветвь при тайм-ауте

Во второй программе одна задача не может завершиться. Сохраните код как concurrency-timeout.tpz:

TOPAZ
function spin() -> int {
    let mut i = 0
    while true { i += 1 }
    i
}

let result = concurrent(timeout: 5ms) {
    stuck: spin()
} else {
    { stuck: -1 }
}

print("{result.stuck}")

Проверьте и запустите его:

BASH
topaz check concurrency-timeout.tpz
topaz run concurrency-timeout.tpz

Когда срок истекает первым, частичные результаты задач не выдаются, а ветвь else предоставляет весь результат:

Вывод
-1

Различаем три исхода

Что произошло до предельного срокаРезультат выражения
Все задачи завершилисьЗапись с одним полем для каждой задачи
Первым истёк тайм-аутЗначение блока else
В задаче возникла ошибка выполненияВсё выражение завершается ошибкой; else не выполняется

Если задача вычислила Err(error), ошибки выполнения не было. Этот Result является обычным значением в именованном поле результата. Запасная ветвь должна иметь тип, совместимый с записью успешных задач, поэтому в обоих примерах успешный путь и else используют одинаковые имена полей и совместимые типы значений.

Чего не гарантируют имена

Имена фиксируют связь с результатами, но не расписание. Топаз не обещает порядок запуска и завершения задач, порядок смешивания вывода, справедливость планировщика или детерминированное чередование. Если порядок важен, не вызывайте print внутри задач: сначала дождитесь общего результата и выводите его после объединения, как в примерах.

Отмена и очистка оставшейся работы после тайм-аута являются политикой среды выполнения. Выражение Топаза не наблюдает более поздние ошибки оставленной работы. Нельзя рассчитывать ни на конкретный планировщик, ни на переносимую очистку после тайм-аута.

Частые исправления

  • Пишите 3s или 5ms без пробела. Форма свойства вроде .seconds не является литералом времени.
  • Форма с тайм-аутом требует else, а простое объединение не принимает else.
  • else обрабатывает только тайм-аут. Она не перехватывает ошибку выполнения и не извлекает значение из Err.
  • Имена задач должны быть уникальными; синтаксиса для частичного результата нет.

Простое объединение concurrent { ... } без тайм-аута уместно на углублённой странице, когда ожидание всех задач и есть предмет примера. Для обычных примеров это не форма по умолчанию.

В Топазе нет неявной асинхронной модели, синтаксиса async/await, открытых пользователю дескрипторов задач, общей арифметики времени и заданного протокола отмены. Наблюдаемые границы ошибок и порядка описаны на странице Поведение среды выполнения, а знакомые формы, которые пока не поддерживаются, — на странице Запрещённые и отложенные формы.