Вернуть запущенную асинхронную<T> в F# функцию из вызова неасинхронной Func<T> из C#?
Допустим, я хочу вызвать из F# эту функцию C#:
public static class Foo {
public Bar Baz()
{
...
}
}
Проблема в том, что эта функция сильно загружает процессор, и я не хочу блокировать ее. К сожалению, библиотека C# не имеет Task<Bar> BazAsync()
перегрузки.
Затем я хочу сам предоставить асинхронную версию, создав функцию F#, которая вызывает ее и возвращает (уже запущенная) Async<Bar>
, То есть я не хочу использовать System.Threading.Task<Bar>
,
Я думаю, что я ищу, это эквивалент Task<T>.Run()
в F# Async способ делать вещи.
Я уже посмотрел на следующие варианты:
Async.StartAsTask
-> имеет дело с типом задачи C#ish.Async.Start
а такжеAsync.StartImmediately
-> получитьAsync<unit>
неAsync<T>
Является Async.StartChild
что я ищу? Если да, то это будет:
let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
Async.StartChild asyncTask
Однако, если это выше, решение:
- Почему в большей части документации по асинхронным рабочим процессам F# не упоминается StartChild?
- Почему BazWrapper возвращается
Async<Async<Bar>>
вместоAsync<Bar>
?
1 ответ
BazWrapper возвращает Async<Async<Bar>>
потому что это то, что делает StartChild: это занимает Async<'T>
и возвращается Async<Async<'T>>
, Намерение состоит в том, чтобы использовать его в выражении асинхронных вычислений, чтобы вы могли запустить несколько "дочерних" асинхронных операций. Пример из примера кода Async.Start vs Async.StartChild:
async {
//(...async stuff...)
for msg in msgs do
let! child = asyncSendMsg msg |> Async.StartChild
()
//(...more async stuff...)
}
Когда вы внутри async
Вычислительное выражение, let!
Ключевое слово будет "развернуть" Async<'Whatever>
оставляя вас со значением типа 'Whatever
, В случае звонка Async.StartChild
, 'Whatever
тип конкретно Async<'T>
,
Поэтому, если вы хотите вернуть уже запущенную асинхронную передачу через Async.StartChild
способ сделать это будет:
let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
async {
let! child = Async.StartChild asyncTask
return! child
}
Однако я подозреваю, что вы обнаружите, что уже запущенная асинхронность не так полезна для вас, как "холодная" асинхронность (еще не запущенная), потому что "холодная" асинхронность все еще может быть составлена с другими асинхронные задачи перед его запуском. (Это может быть полезно, например, для оборачивания журналирования вокруг ваших асинхронных элементов.) Поэтому, если бы я писал код для вашей ситуации, я бы, вероятно, просто сделал это:
let BazWrapper() =
async {
return Foo.Bar()
}
И теперь BazWrapper() возвращает Async, который еще не был запущен, и вы можете запустить его с Async.RunSynchronously
если вы хотите сразу получить значение или использовать его в другом async
Вычислительные выражения.