Jump to content

Асинхронный/ожидание

(Перенаправлено с «Ожидание» )

В компьютерном программировании шаблон async/await — это синтаксическая особенность многих языков программирования , которая позволяет асинхронную неблокирующую структурировать функцию аналогично обычной синхронной функции. Он семантически связан с концепцией сопрограммы и часто реализуется с использованием аналогичных методов и в первую очередь предназначен для предоставления программе возможности выполнять другой код во время ожидания завершения длительной асинхронной задачи, обычно представленной обещаниями или аналогичные структуры данных . Эта функция есть в C# , [1] : 10  C++ , Python , F# , Hack , Julia , Dart , Kotlin , Rust , [2] Nim , [3] JavaScript и Swift . [4]

В F# добавлены асинхронные рабочие процессы с точками ожидания в версии 2.0 в 2007 году. [5] Это повлияло на механизм async/await, добавленный в C#. [6]

Microsoft впервые выпустила версию C# с async/await в Async CTP (2011). Позже он был официально выпущен в C# 5 (2012). [7] [1] : 10 

Ведущий разработчик Haskell Саймон Марлоу создал пакет async в 2012 году. [8]

Python добавил поддержку async/await в версии 3.5 в 2015 году. [9] добавление 2 новых ключевых слов , async и await.

В TypeScript добавлена ​​поддержка async/await в версии 1.7 в 2015 году. [10]

В Javascript добавлена ​​поддержка async/await в 2017 году как часть версии ECMAScript 2017 JavaScript.

В Rust добавлена ​​поддержка async/await в версии 1.39.0 в 2019 году с помощью async ключевое слово и .await постфиксный оператор, оба они представлены в версии языка 2018 года. [11]

В C++ добавлена ​​поддержка async/await в версии 20 в 2020 году с 3 новыми ключевыми словами. co_return, co_await, co_yield.

Swift добавил поддержку async/await в версии 5.5 в 2021 году, добавив 2 новых ключевых слова. async и await. Он был выпущен вместе с конкретной реализацией модели Актера с actor ключевое слово [12] который использует async/await для обеспечения доступа к каждому актеру извне.

Пример С#

[ редактировать ]

Приведенная ниже функция C# , которая загружает ресурс по URI и возвращает длину ресурса, использует этот шаблон async/await:

public async Task<int> FindPageSizeAsync(Uri uri) {    var client = new HttpClient();    byte[] data = await client.GetByteArrayAsync(uri);    return data.Length;}
  • Во-первых, async ключевое слово указывает C#, что метод является асинхронным, то есть он может использовать произвольное количество await выражения и свяжет результат с обещанием . [1] : 165–168 
  • Тип возвращаемого значения , Task<T>, является аналогом концепции обещания в C#, и здесь указано, что результирующее значение имеет тип int.
  • Первое выражение, которое будет выполнено при вызове этого метода, будет new HttpClient().GetByteArrayAsync(uri), [13] : 189–190, 344  [1] : 882  это еще один асинхронный метод, возвращающий Task<byte[]>. Поскольку этот метод является асинхронным, он не загружает весь пакет данных перед возвратом. Вместо этого он начнет процесс загрузки с использованием неблокирующего механизма (например, фонового потока ) и немедленно вернет неразрешенное, неотклоненное сообщение. Task<byte[]> к этой функции.
  • С await ключевое слово, прикрепленное к Task, эта функция немедленно приступит к возврату Task<int> вызывающему абоненту, который затем может продолжить обработку по мере необходимости.
  • Один раз GetByteArrayAsync() завершит загрузку, это устранит проблему Task он вернулся с загруженными данными. Это вызовет обратный вызов и вызовет FindPageSizeAsync() продолжить выполнение, присвоив это значение data.
  • Наконец, метод возвращает data.Length, простое целое число, указывающее длину массива. Компилятор интерпретирует это как решение Task он вернулся раньше, запустив обратный вызов в вызывающем методе, чтобы что-то сделать с этим значением длины.

Функция, использующая async/await, может использовать столько await выражения по своему усмотрению, и каждое из них будет обрабатываться одинаково (хотя обещание будет возвращено вызывающей стороне только для первого ожидания, тогда как для каждого другого ожидания будут использоваться внутренние обратные вызовы). Функция также может напрямую хранить объект обещания и сначала выполнять другую обработку (включая запуск других асинхронных задач), откладывая ожидание обещания до тех пор, пока не понадобится его результат. Функции с обещаниями также имеют методы агрегирования обещаний, которые позволяют программе ожидать несколько обещаний одновременно или по некоторому специальному шаблону (например, в C#). Task.WhenAll(), [1] : 174–175  [13] : 664–665  который возвращает бесполезное значение Task это разрешается, когда все задачи в аргументах решены). Многие типы обещаний также имеют дополнительные функции, помимо тех, которые обычно использует шаблон async/await, например, возможность настроить более одного обратного вызова результата или проверить ход особенно длительной задачи.

В конкретном случае C# и во многих других языках с этой языковой функцией шаблон async/await не является основной частью среды выполнения языка, а вместо этого реализуется с помощью лямбда-выражений или продолжений во время компиляции. Например, компилятор C#, скорее всего, преобразует приведенный выше код во что-то вроде следующего, прежде чем переводить его в формат байт-кода IL :

public Task<int> FindPageSizeAsync(Uri uri) {    var client = new HttpClient();    Task<byte[]> dataTask = client.GetByteArrayAsync(uri);    Task<int> afterDataTask = dataTask.ContinueWith((originalTask) => {        return originalTask.Result.Length;    });    return afterDataTask;}

По этой причине, если метод интерфейса должен вернуть объект-промис, но сам по себе не требует await в теле для ожидания выполнения каких-либо асинхронных задач, ему не требуется async модификатор и вместо этого может напрямую возвращать объект обещания. Например, функция может предоставить обещание, которое немедленно преобразуется в некоторое значение результата (например, в C# Task.FromResult()[13] : 656  ), или он может просто вернуть обещание другого метода, которое окажется именно тем обещанием, которое необходимо (например, при отсрочке на перегрузку ).

Однако одно важное предостережение относительно этой функциональности заключается в том, что, хотя код и напоминает традиционный блокирующий код, на самом деле он неблокирующий и потенциально многопоточный, а это означает, что во время ожидания обещания, предназначенного для await решить. Например, следующий код всегда успешно реализует модель блокировки без await, могут возникнуть промежуточные события во время await и, таким образом, может обнаружить, что общее состояние изменилось из-под него:

var a = state.a;var client = new HttpClient();var data = await client.GetByteArrayAsync(uri);Debug.Assert(a == state.a); // Potential failure, as value of state.a may have been changed                            // by the handler of potentially intervening event.return data.Length;

Реализации

[ редактировать ]

В 2007 году в F# были добавлены асинхронные рабочие процессы версии 2.0. [14] Асинхронные рабочие процессы реализованы как CE ( выражения вычислений ). Их можно определить без указания какого-либо специального контекста (например, async в С#). В асинхронных рабочих процессах F# к ключевым словам добавляется значок (!) для запуска асинхронных задач.

Следующая асинхронная функция загружает данные с URL-адреса, используя асинхронный рабочий процесс:

let asyncSumPageSizes (uris: #seq<Uri>) : Async<int> = async {    use httpClient = new HttpClient()    let! pages =         uris        |> Seq.map(httpClient.GetStringAsync >> Async.AwaitTask)        |> Async.Parallel    return pages |> Seq.fold (fun accumulator current -> current.Length + accumulator) 0}

В 2012 году C# добавил шаблон async/await в C# версии 5.0, который Microsoft называет асинхронным шаблоном на основе задач (TAP). [15] Асинхронные методы обычно возвращают либо void, Task, Task<T>, [13] : 35  [16] : 546–547  [1] : 22, 182  ValueTask или ValueTask<T>. [13] : 651–652  [1] : 182–184  Пользовательский код может определять пользовательские типы, которые асинхронные методы могут возвращать с помощью пользовательских конструкторов асинхронных методов, но это сложный и редкий сценарий. [17] Асинхронные методы, которые возвращают void предназначены для обработчиков событий ; в большинстве случаев, когда синхронный метод возвращает void, возвращаясь Task вместо этого рекомендуется использовать его, поскольку он обеспечивает более интуитивную обработку исключений. [18]

Методы, в которых используются await должно быть объявлено с помощью async ключевое слово. В методах, которые имеют возвращаемое значение типа Task<T>, методы, объявленные с помощью async должен иметь оператор возврата типа, назначаемого T вместо Task<T>; компилятор помещает значение в Task<T> общий. Также возможно await методы, которые имеют тип возвращаемого значения Task или Task<T> которые объявлены без async.

Следующий асинхронный метод загружает данные с URL-адреса, используя await. Поскольку этот метод выдает задачу для каждого URI, прежде чем требовать завершения с помощью await ключевое слово, ресурсы могут загружаться одновременно, не дожидаясь завершения последнего ресурса, прежде чем начинать загрузку следующего.

public async Task<int> SumPageSizesAsync(IEnumerable<Uri> uris) {    var client = new HttpClient();    int total = 0;    var loadUriTasks = new List<Task<byte[]>>();    foreach (var uri in uris)    {        var loadUriTask = client.GetByteArrayAsync(uri);        loadUriTasks.Add(loadUriTask );    }    foreach (var loadUriTask in loadUriTasks)    {        statusText.Text = $"Found {total} bytes ...";        var resourceAsBytes = await loadUriTask;        total += resourceAsBytes.Length;    }    statusText.Text = $"Found {total} bytes total";    return total;}

Питон 3.5 (2015 г.) [19] добавлена ​​поддержка async/await, как описано в PEP 492 (написано и реализовано Юрием Селивановым ). [20]

import asyncioasync def main():    print("hello")    await asyncio.sleep(1)    print("world")asyncio.run(main())

Оператор await в JavaScript можно использовать только внутри асинхронной функции или на верхнем уровне модуля . Если параметром является промис , выполнение асинхронной функции возобновится, когда промис будет разрешен (если промис не будет отклонен, в этом случае будет выдана ошибка, которую можно обработать с помощью обычной обработки исключений JavaScript ). Если параметр не является обещанием, сам параметр будет возвращен немедленно. [21]

Многие библиотеки предоставляют объекты-промисы, которые также можно использовать с await, если они соответствуют спецификации собственных промисов JavaScript. Однако промисы из библиотеки jQuery не были совместимы с Promises/A+ до версии jQuery 3.0. [22]

Вот пример (измененный из этого [23] статья):

async function createNewDoc() {  let response = await db.post({}); // post a new doc  return db.get(response.id); // find by id}async function main() {  try {    let doc = await createNewDoc();    console.log(doc);  } catch (err) {    console.log(err);  }}main();

Node.js версии 8 включает утилиту, которая позволяет использовать методы на основе обратного вызова стандартной библиотеки в качестве обещаний. [24]

В C++ await (названный в C++ co_await) был официально объединен с версией 20 . [25] Поддержка этого, сопрограмм и таких ключевых слов, как co_await доступны в компиляторах GCC и MSVC , а Clang имеет частичную поддержку.

Стоит отметить, что std::promise и std::future, хотя и могут показаться ожидаемыми объектами, не реализуют ни одного механизма, необходимого для возврата из сопрограмм и ожидания с использованием co_await. Программистам необходимо реализовать ряд публичных функций-членов, таких как await_ready, await_suspend, и await_resume по типу возвращаемого значения, чтобы тип ожидался. Подробности можно найти на cppreference. [26]

#include <iostream>#include "CustomAwaitableTask.h"using namespace std;CustomAwaitableTask<int> add(int a, int b){    int c = a + b;    co_return c;}CustomAwaitableTask<int> test(){    int ret = co_await add(1, 2);    cout << "return " << ret << endl;    co_return ret;}int main(){    auto task = test();    return 0;}

Язык C не поддерживает await/async. Некоторые библиотеки сопрограмм, такие как s_task [27] имитируйте ключевые слова await/async с помощью макросов.

#include <stdio.h>#include "s_task.h"// define stack memory for tasksint g_stack_main[64 * 1024 / sizeof(int)];int g_stack0[64 * 1024 / sizeof(int)];int g_stack1[64 * 1024 / sizeof(int)];void sub_task(__async__, void* arg) {    int i;    int n = (int)(size_t)arg;    for (i = 0; i < 5; ++i) {        printf("task %d, delay seconds = %d, i = %d\n", n, n, i);        s_task_msleep(__await__, n * 1000);        //s_task_yield(__await__);    }}void main_task(__async__, void* arg) {    int i;    // create two sub-tasks    s_task_create(g_stack0, sizeof(g_stack0), sub_task, (void*)1);    s_task_create(g_stack1, sizeof(g_stack1), sub_task, (void*)2);    for (i = 0; i < 4; ++i) {        printf("task_main arg = %p, i = %d\n", arg, i);        s_task_yield(__await__);    }    // wait for the sub-tasks for exit    s_task_join(__await__, g_stack0);    s_task_join(__await__, g_stack1);}int main(int argc, char* argv) {    s_task_init_system();    //create the main task    s_task_create(g_stack_main, sizeof(g_stack_main), main_task, (void*)(size_t)argc);    s_task_join(__await__, g_stack_main);    printf("all task is over\n");    return 0;}


В Перле 5

[ редактировать ]

Будущее::AsyncAwait [28] Модуль стал предметом гранта Perl Foundation в сентябре 2018 года. [29]

В ржавчине

[ редактировать ]

7 ноября 2019 года async/await был выпущен в стабильной версии Rust. [30] Асинхронные функции в Rust превращают сахар в простые функции, возвращающие значения, реализующие черту Future. В настоящее время они реализованы с помощью конечного автомата . [31]

// In the crate's Cargo.toml, we need `futures = "0.3.0"` in the dependencies section,// so we can use the futures crateextern crate futures; // There is no executor currently in the `std` library.// This desugars to something like// `fn async_add_one(num: u32) -> impl Future<Output = u32>`async fn async_add_one(num: u32) -> u32 {    num + 1}async fn example_task() {    let number = async_add_one(5).await;    println!("5 + 1 = {}", number);}fn main() {    // Creating the Future does not start the execution.    let future = example_task();    // The `Future` only executes when we actually poll it, unlike Javascript.    futures::executor::block_on(future);}

В Свифте

[ редактировать ]

Свифт 5.5 (2021 г.) [32] добавлена ​​поддержка async/await, как описано в SE-0296. [33]

func getNumber() async throws -> Int {    try await Task.sleep(nanoseconds: 1_000_000_000)    return 42}Task {    let first = try await getNumber()    let second = try await getNumber()    print(first + second)}

Преимущества и критика

[ редактировать ]

Шаблон async/await особенно привлекателен для разработчиков языков, которые не имеют или не контролируют собственную среду выполнения, поскольку async/await может быть реализован исключительно как преобразование в конечный автомат в компиляторе. [34]

Сторонники утверждают, что асинхронный неблокирующий код можно написать с помощью async/await, который выглядит почти как традиционный синхронный блокирующий код. В частности, утверждалось, что ожидание — лучший способ написания асинхронного кода в передачи сообщений программах ; в частности, в качестве ожидаемых преимуществ были названы близость к блокирующему коду, читаемость и минимальное количество шаблонного кода . [35] В результате async/await позволяет большинству программистов легче рассуждать о своих программах, а await имеет тенденцию способствовать созданию более качественного и надежного неблокирующего кода в приложениях, которые этого требуют. [ сомнительно обсудить ]

Критики async/await отмечают, что этот шаблон также имеет тенденцию вызывать асинхронность окружающего кода; и что его заразная природа разделяет библиотечные экосистемы языков на синхронные и асинхронные библиотеки и API - проблему, часто называемую «раскраской функций». [36] Альтернативы async/await, которые не страдают от этой проблемы, называются «бесцветными». Go Примеры бесцветного дизайна включают горутины Java и виртуальные потоки . [37]

См. также

[ редактировать ]
  1. ^ Перейти обратно: а б с д и ж г Скит, Джон (23 марта 2019 г.). C# в глубине . Мэннинг. ISBN  978-1617294532 .
  2. ^ «Анонсируем Rust 1.39.0» . Проверено 07.11.2019 .
  3. ^ «Вышла версия 0.9.4 — блог Nim» . Проверено 19 января 2020 г.
  4. ^ «Параллелизм — язык программирования Swift (Swift 5.5)» . docs.swift.org . Проверено 28 сентября 2021 г.
  5. ^ Сайм, Дон; Петричек, Томас; Ломов, Дмитрий (2011). «Модель асинхронного программирования F#» . Практические аспекты декларативных языков . Конспекты лекций по информатике. Том. 6539. Спрингер Линк. стр. 175–189. дои : 10.1007/978-3-642-18378-2_15 . ISBN  978-3-642-18377-5 . Проверено 29 апреля 2021 г.
  6. ^ «Ранняя история F #, HOPL IV» . Цифровая библиотека ACM . Проверено 29 апреля 2021 г.
  7. ^ Хейлсберг, Андерс. «Андерс Хейлсберг: Знакомство с Async – упрощение асинхронного программирования» . Канал 9 MSDN . Майкрософт . Проверено 5 января 2021 г.
  8. ^ «async: асинхронно запускать операции ввода-вывода и ждать их результатов» . Хакадж .
  9. ^ «Что нового в Python 3.5 — документация Python 3.9.1» . docs.python.org . Проверено 5 января 2021 г.
  10. ^ Гаурав, Сет (30 ноября 2015 г.). «Анонс TypeScript 1.7» . Типскрипт . Майкрософт . Проверено 5 января 2021 г.
  11. ^ Мацакис, Нико. «Асинхронное ожидание в стабильной версии Rust! | Блог Rust» . blog.rust-lang.org . Ржавый блог . Проверено 5 января 2021 г.
  12. ^ «Параллелизм — язык программирования Swift (Swift 5.6)» .
  13. ^ Перейти обратно: а б с д и Альбахари, Джозеф (2022). C# 10 в двух словах О'Рейли. ISBN  978-1-098-12195-2 .
  14. ^ «Знакомство с асинхронными рабочими процессами F#» . 10 октября 2007 г.
  15. ^ «Асинхронный шаблон на основе задач» . Майкрософт . Проверено 28 сентября 2020 г.
  16. ^ Прайс, Марк Дж. (2022). C# 8.0 и .NET Core 3.0 — современная кроссплатформенная разработка: создавайте приложения с помощью C#, .NET Core, Entity Framework Core, ASP.NET Core и ML.NET с помощью кода Visual Studio . Пакет. ISBN  978-1-098-12195-2 .
  17. ^ Тепляков, Сергей (11 января 2018 г.). «Расширение асинхронных методов в C#» . Поддержка разработчиков . Проверено 30 октября 2022 г.
  18. ^ Стивен Клири, Async/Await - Лучшие практики асинхронного программирования
  19. ^ «Выпуск Python 3.5.0» .
  20. ^ «PEP 492 — сопрограммы с синтаксисом async и await» .
  21. ^ «ожидание — JavaScript (MDN)» . Проверено 2 мая 2017 г.
  22. ^ «Руководство по обновлению jQuery Core 3.0» . Проверено 2 мая 2017 г.
  23. ^ «Укрощение асинхронного зверя с помощью ES7» . Проверено 12 ноября 2015 г.
  24. ^ Foundation, Node.js (30 мая 2017 г.). «Узел v8.0.0 (текущий) — Node.js» . Нод.js.
  25. ^ «Комитет ISO C++ объявляет, что разработка C++20 теперь завершена» . 25 февраля 2019 г.
  26. ^ «Сопрограммы (C++20)» .
  27. ^ «s_task — ожидаемая библиотека сопрограмм для C» . Гитхаб .
  28. ^ «Future::AsyncAwait — синтаксис отложенной подпрограммы для фьючерсов» .
  29. ^ «Голосование по грантам в сентябре 2018 г. — The Perl Foundation» . news.perlfoundation.org . Проверено 26 марта 2019 г.
  30. ^ Мацакис, Нико. «Асинхронное ожидание в стабильной версии Rust!» . Ржавый блог . Проверено 7 ноября 2019 г.
  31. ^ Опперманн, Филипп. «Асинхронный/Ожидание» . Проверено 28 октября 2020 г.
  32. ^ «Архивная копия» . Архивировано из оригинала 23 января 2022 г. Проверено 20 декабря 2021 г. {{cite web}}: CS1 maint: архивная копия в заголовке ( ссылка )
  33. ^ «СЭ-0296» . Гитхаб .
  34. ^ «Асинхронность, часть 3. Как компилятор C# реализует асинхронные функции» .
  35. ^ Заяц "Никаких ошибок". Восемь способов обработки неблокирующих возвратов в программах передачи сообщений CPPCON, 2018
  36. ^ «Какого цвета ваша функция?» .
  37. ^ «Виртуальные темы» .
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: bc569204db5f0313155700ea038b530d__1722259260
URL1:https://arc.ask3.ru/arc/aa/bc/0d/bc569204db5f0313155700ea038b530d.html
Заголовок, (Title) документа по адресу, URL1:
Async/await - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)