Ответ 1
Это на самом деле ожидаемое поведение - хотя и не совсем очевидное!
Конструкция use
предоставляет ресурс, когда выполнение асинхронного рабочего процесса выходит из текущей области. Это то же самое, что и поведение use
вне асинхронных рабочих процессов. Проблема в том, что рекурсивный вызов (вне асинхронного) или рекурсивный вызов с использованием return!
(внутри async) не означает, что вы покидаете область видимости. Таким образом, в этом случае ресурс удаляется только после возврата рекурсивного вызова.
Чтобы проверить это, я использую помощник, который печатает при удалении:
let tester () =
{ new System.IDisposable with
member x.Dispose() = printfn "bye" }
Следующая функция завершает рекурсию после 10 итераций. Это означает, что он продолжает выделять ресурсы и распоряжается всеми ими только после завершения всего рабочего процесса:
let rec loop(n) = async {
if n < 10 then
use t = tester()
do! Async.Sleep(1000)
return! loop(n+1) }
Если вы запустите это, он будет работать в течение 10 секунд, а затем распечатать 10 раз "пока" - это связано с тем, что выделенные ресурсы по-прежнему находятся в области во время рекурсивных вызовов.
В вашем примере функция using
более явно ограничивает область. Однако вы можете сделать то же самое, используя вложенный асинхронный рабочий процесс. Ниже приведен только ресурс в области при вызове метода Sleep
и поэтому он распоряжается им до рекурсивного вызова:
let rec loop(n) = async {
if n < 10 then
do! async {
use t = tester()
do! Async.Sleep(1000) }
return! loop(n+1) }
Аналогично, когда вы используете цикл for
или другие конструкции, которые ограничивают область действия, ресурс немедленно устанавливается:
let rec loop(n) = async {
for i in 0 .. 10 do
use t = tester()
do! Async.Sleep(1000) }