Как на самом деле работает асинхронность F #?

Я пытаюсь узнать, как async и let! работают в F #. Все документы, которые я прочитал, кажутся запутанными. Какой смысл запускать асинхронный блок с Async.RunSynchronously? Это асинхронно или синхронно? Выглядит как противоречие.

Документация говорит, что Async.StartImmediate выполняется в текущем потоке. Если он работает в одном и том же потоке, он не выглядит для меня слишком асинхронным ... А может быть, асинхронные больше похожи на сопрограммы, чем на потоки. Если да, то когда они возвращают четвертое?

Цитируя MS docs:

Строка кода, которая использует let! начинает вычисления, а затем поток приостанавливается пока результат не станет доступен, и в этот момент выполнение продолжается.

Если поток ожидает результата, зачем мне его использовать? Выглядит как вызов простой старой функции.

А что делает Async.Parallel? Он получает последовательность Async <'T>. Почему бы не последовательность простых функций, выполняемых параллельно?

Я думаю, что мне здесь не хватает чего-то очень простого. Думаю, после того, как я это пойму, вся документация и примеры станут понятны.

34
задан marcus 12 December 2012 в 23:45
поделиться

4 ответа

Недавно я сделал краткий обзор функций в модуле Async: здесь . Может, это поможет.

6
ответ дан 7 July 2019 в 16:31
поделиться

Асинхронный режим используется для экономии количества используемых потоков.

См. Следующий пример:

let fetchUrlSync url = 
    let req = WebRequest.Create(Uri url)
    use resp = req.GetResponse()
    use stream = resp.GetResponseStream()
    use reader = new StreamReader(stream)
    let contents = reader.ReadToEnd()
    contents 

let sites = ["http://www.bing.com";
             "http://www.google.com";
             "http://www.yahoo.com";
             "http://www.search.com"]

// execute the fetchUrlSync function in parallel 
let pagesSync = sites |> PSeq.map fetchUrlSync  |> PSeq.toList

Приведенный выше код - это то, что вы хотите сделать: определить функцию и выполнить ее параллельно. Так зачем нам здесь асинхронный режим?

Давайте рассмотрим что-нибудь большое. Например. если количество сайтов не 4, а скажем 10 000! Затем требуется 10 000 потоков для их параллельного запуска, что является огромными затратами ресурсов.

В асинхронном режиме:

let fetchUrlAsync url =
    async { let req =  WebRequest.Create(Uri url)
            use! resp = req.AsyncGetResponse()
            use stream = resp.GetResponseStream()
            use reader = new StreamReader(stream)
            let contents = reader.ReadToEnd()
            return contents }
let pagesAsync = sites |> Seq.map fetchUrlAsync |> Async.Parallel |> Async.RunSynchronously

Когда код находится в , используйте! resp = req.AsyncGetResponse () , текущий поток прекращает работу, и его ресурс может использоваться для других целей. Если ответ вернется через 1 секунду, ваш поток может использовать эту 1 секунду для обработки других вещей. В противном случае поток блокируется, тратя ресурсы потока на 1 секунду.

Таким образом, даже если вы загружаете 10000 веб-страниц параллельно асинхронным способом, количество потоков ограничено небольшим числом.

Я думаю, что вы не программист .Net / C #. В учебном пособии по асинхронности обычно предполагается, что вы знаете .Net и знаете, как программировать асинхронный ввод-вывод на C # (много кода). Магия конструкции Async в F # не для параллелизма. Поскольку простая параллель может быть реализована другими конструкциями, например ParallelFor в параллельном расширении .Net. Однако асинхронный ввод-вывод более сложен, поскольку вы видите, что поток прекращает свое выполнение, когда ввод-вывод завершается, ввод-вывод должен разбудить свой родительский поток. Здесь используется асинхронная магия: в нескольких строках сжатого кода вы можете выполнять очень сложный контроль.

12
ответ дан 27 November 2019 в 16:52
поделиться

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

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

Вы можете сделать это как три потока, но тогда вам нужно будет отслеживать и возобновлять исходный поток, когда завершится третий, но это больше работы, проще, чтобы .NET framework позаботился об этом.

0
ответ дан 27 November 2019 в 16:52
поделиться

Идея let! и Async.RunSynchronously означает, что иногда у вас есть асинхронная операция, результаты которой вам нужны, прежде чем вы сможете продолжить. Например, функция «загрузить веб-страницу» может не иметь синхронного эквивалента, поэтому вам нужно каким-то образом запустить ее синхронно. Или, если у вас есть Async.Parallel , у вас могут быть сотни задач, которые все выполняются одновременно, но вы хотите, чтобы все они были выполнены, прежде чем продолжить.

Насколько я могу судить, причина, по которой вы должны использовать Async.StartImmediate , заключается в том, что у вас есть некоторые вычисления, которые вам нужно выполнить в текущем потоке (возможно, потоке пользовательского интерфейса), не блокируя его. Используются ли сопрограммы? Я предполагаю, что вы могли бы назвать это так, хотя в .Net нет общего механизма сопрограмм.

Так почему же Async.Parallel требует последовательности Async <'T> ? Вероятно, потому что это способ создания Async <'T> объектов. Вы можете легко создать свою собственную абстракцию, которая работает с простыми функциями (или комбинацией простых функций и Async s, но это будет просто вспомогательная функция.

1
ответ дан 27 November 2019 в 16:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: