I have a blocking call blockingFoo() that I would like to use in an async context. I would like to run it on another thread, so as to not block the async.
Here is my solution:
let asyncFoo =
async {
blockingFoo() |> ignore
}
|> Async.StartAsTask
|> Async.AwaitTask
Is this the correct way to do this?
Will this work as expected?
I think you're a bit lost. Async.StartAsTask followed by Async.AwaitTask effectively cancel each other, with the side-effect that the Task created in the process actually triggers evaluation of the async block containing blockingFoo on the thread pool. So it works, but in a way that betrays expectations.
If you want to trigger evaluation of asyncFoo from within another async block, a more natural way to do it would be to use Async.Start if you don't want to await its completion, or Async.StartChild if you do.
let asyncFoo =
async {
blockingFoo() |> ignore
}
async {
// "fire and forget"
asyncFoo |> Async.Start
// trigger the computation
let! comp = Async.StartChild asyncFoo
// do other work here while comp is executing
// await the results of comp
do! comp
}
Related
I'm trying to execute several sqlx queries in parallel given by a iterator.
This is probably the closest I've got so far.
let mut futures = HahshMap::new() // placeholder, filled HashMap in reality
.iter()
.map(async move |(_, item)| -> Result<(), sqlx::Error> {
let result = sqlx::query_file_as!(
// omitted
)
.fetch_one(&pool)
.await?;
channel.send(Enum::Event(result)).ignore();
Ok(())
})
.clollect();
futures::future::join_all(futures);
All queries and sends are independent from each other, so if one of them fails, the others should still get processed.
Futthermore the current async closure is not possible like this.
Rust doesn't yet have async closures. You instead need to have the closure return an async block:
move |(_, item)| async move { ... }
Additionally, make sure you .await the future returned by join_all in order to ensure the individual tasks are actually polled.
I'm trying to use async-std's task::spawn:
use std::time::Duration;
async fn w() {
loop {
task::sleep(Duration::from_secs(1)).await;
println!("Tick");
}
}
#[async_std::main]
async fn main() {
println!("Start");
task::spawn(w()).await;
println!("End");
}
I expect that "End" is immediately printed after "Start", but the "Tick" loop is printed endlessly.
So what is exactly the difference to the following?
#[async_std::main]
async fn main() {
println!("Start");
w().await;
println!("End");
}
Don't .await the spawned task. Doing so is like joining a thread: it waits for the task to finish before continuing. The handle spawn returns does not need to be awaited in order for the task to be driven to completion.
task::spawn(w()).await;
As written, you are correct that task::spawn(w()).await is no different than w().await. They both block the thread waiting for w() to finish.
What's the purpose of awaiting a spawn call, then? It's useful if you want to spawn a background task, do some other work on the current thread, and then block.
let handle = task::spawn(w());
do_other_things_for_a_while();
// Block and retrieve the result.
let result = handle.await;
This program work fine:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
But when I put the same code in the async block when I want to deal with tcp client:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let listener=new TcpListener(IPAddress.Parse("127.0.0.1"),2000)
let private loop(client:TcpClient,sr:StreamReader,sw:StreamWriter)=
async{
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let private startLoop()=
while true do
let client=listener.AcceptTcpClient()
let stream=client.GetStream()
let sr=new StreamReader(stream)
let sw=new StreamWriter(stream)
sw.AutoFlush<-true
Async.Start(loop(client,sr,sw))|>ignore
listener.Start()
startLoop()
listener.Stop()
the timer function will not quit when it have run three times.I want to know why?Thanks
I first want to mention a few things, instead of using Console.WriteLine("a"), just use printfn "a". Secondly, the snippet of code you gave does not terminate, so if you try it in FSI, it will continue running after the main thread finishes. This is likely not an issue in a console app. To answer your question, it has to do with async workflow. If you like in this article: Async Programming, you'll notice that they spawn the async computation as a child and then perform an async sleep to give the child a chance to start. This has to do with the way tasks are scheduled. .NET Frameworks use a "work-first" policy. Continuations typically don't get executed until a blocking event forces the thread to give up the current task. This is how I got the timer event to run:
open System
open System.Threading
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
printfn "a"
inc<-inc+1
if inc=3 then
printfn "hit 3!"
//autoEvent.Set()|>ignore
let private loop i =
async{
printfn "Started as child..."
let aWrap(o:obj) = // so that we can see which child prints
printfn "%d" i
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(aWrap,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let startLoopAsync() =
async {
let children =
[1..3]
|> List.map(fun i ->
Async.StartChild(loop i) // start as child
)
do! Async.Sleep 100 // give chance for children to start
children
|> List.iter (Async.RunSynchronously >> ignore) // wait for all children
}
startLoopAsync() |> (Async.RunSynchronously >> ignore) // wait for async loop start
Thread.Sleep(5000)
Note that I used StartChild. I recommend this because of the facts noted here: Async.Start vs. Async.StartChild. A child async task does not need to be given its own cancellation token. Instead it inherits from its parent. So, if I had assigned a cancellation token to the startLoopAsync(), I could cancel that task and all children would cancel as well. Lastly, I recommend keeping a handle on timer in case you ever need to stop that re-occurring event. Not keeping a handle would result in not being able to stop it without killing the process. That is what Thread.Sleep(5000) was for. To show that after the async tasks finish, the timers keep triggering events until the process dies (which requires killing FSI if you use that to test).
I hope this answers your question,
Cheers!
I'm trying to consume a C# library in F#. The library makes heavy use of async/await. I want to use within an async { ... } workflow in F#.
I see we can Async.AwaitTask on async C# methods returning Task<T>, but what about those returning plain Task?
Perhaps, is there a helper to convert these to Async<unit> or to convert Task to Task<unit> so it will work with Async.AwaitTask?
You can use ContinueWith:
let awaitTask (t: Task) = t.ContinueWith (fun t -> ()) |> Async.AwaitTask
Or AwaitIAsyncResult with infinite timeout:
let awaitTask (t: Task) = t |> Async.AwaitIAsyncResult |> Async.Ignore
Update:
The FSharp.Core library for F# 4.0 now includes an Async.AwaitTask overload that accepts a plain Task. If you're using F# 4.0 then you should use this core function instead of the code below.
Original answer:
If your task could throw an exception then you probably also want to check for this. e.g.
let awaitTask (task : Task) =
async {
do! task |> Async.AwaitIAsyncResult |> Async.Ignore
if task.IsFaulted then raise task.Exception
return ()
}
Update:
The FSharp.Core library for F# 4.0 now includes an Async.AwaitTask overload that accepts a plain Task. If you're using F# 4.0 then you should use this core function instead of the code below.
Original answer:
I really liked Ashley's suggestion using function composition. Additionally, you can extend the Async module like this:
module Async =
let AwaitTaskVoid : (Task -> Async<unit>) =
Async.AwaitIAsyncResult >> Async.Ignore
Then it appears in Intellisense along with Async.AwaitTask. It can be used like this:
do! Task.Delay delay |> Async.AwaitTaskVoid
Any suggestions for a better name?
To properly propagate both exceptions and cancellation properly, I think you need something like this (partially based on deleted answer by Tomáš Petříček):
module Async =
let AwaitVoidTask (task : Task) : Async<unit> =
Async.FromContinuations(fun (cont, econt, ccont) ->
task.ContinueWith(fun task ->
if task.IsFaulted then econt task.Exception
elif task.IsCanceled then ccont (OperationCanceledException())
else cont ()) |> ignore)
I have an async computation like the following (see inline comments):
async {
//...
do! Async.Sleep(100) //cancellation may happen during sleep
//... but isn't checked at the end of the sleep, so regular, non-async computations are executed here
}
In order to force a cancellation check / terminate the entire async computation before the "regular" computation part is reached, I insert an effective no-op do! Async.Sleep(1) immediately after the do! Async.Sleep(100). Is there a cleaner way to do this? Possibly even something like do! Async.Nop.
How about something like this:
let nop = async.Return ()
Then you could use it like:
async {
// ...
do! Async.Sleep 100
do! nop
}