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

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

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

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

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

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

TOPAZ
let результат = concurrent(timeout: 3s) {
    итого: 1 + 2
    метка: "ready"
} else {
    { итого: -1, метка: "timeout" }
}

print("{результат.итого} {результат.метка}")

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

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

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

Вывод
3 ready

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

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

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

TOPAZ
function крутиться() -> int {
    let mut счётчик = 0
    while true { счётчик += 1 }
    счётчик
}

let результат = concurrent(timeout: 5ms) {
    зависло: крутиться()
} else {
    { зависло: -1 }
}

print("{результат.зависло}")

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

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, доступные пользователю дескрипторы задач, общая арифметика времени и фиксированный протокол отмены. Наблюдаемые границы ошибок и порядка выполнения описаны на странице Поведение среды выполнения, а знакомые синтаксические формы, которые пока не поддерживаются, — на странице Запрещённые и отложенные формы.