F# 6 .NET tasks - how to use - f#

F# 6 introduced support for .NET tasks.
This snippet is shared everywhere - but what's the right way to call and use the result of the task (to e.g. printf it)?
let readFilesTask (path1, path2) =
task {
let! bytes1 = File.ReadAllBytesAsync(path1)
let! bytes2 = File.ReadAllBytesAsync(path2)
return Array.append bytes1 bytes2
}
One option that works is:
let result = readFilesTask ("filaA", "fileB")
printf $"%A{result.Result}"
But is this the intended way of use?

There are a few ways to work with tasks. What is the "intended" way will depend on what you are trying to achieve.
Also note that for many applications, async is only marginally slower but much easier to work with because it is "cold".
You can consume a task in another task using let! and do!:
task {
let! x = someOtherTask ()
do! andAnotherTask ()
return x
}
You can start a task and not wait on the result (fire and forget):
let foo () =
task {
printfn "Done."
}
foo () |> ignore // tasks are "hot" unlike asyncs
You can turn a task into an Async<'t> using Async.AwaitTask:
async {
do!
task {
printfn "Done."
}
|> Async.AwaitTask
}
You can block the current thread until a task is complete:
let foo () =
task {
printfn "Done."
}
let t = foo ()
t.Result
I expect that most real applications will have lots of do! and let! statements, but only a few .Result calls in order to avoid blocking.

Related

How to ignore a Task in F#?

If we have a function f that returns Async<something>, we can ignore the return value with do! f() |> Async.Ignore. Is there a similar construct for ignoring Tasks? Right now, I'm having to do something like this:
task {
let! result = SomeOperation()
result |> ignore
}
Technically I could do |> Async.AwaitTask |> Async.Ignore, but I'm not sure how wasteful it is to convert a task to async just to ignore it...
If it matters, I'm using task{} computation expressions from Ply.
You can wait for a task to complete using the Result property:
let t =
task {
let! _ = SomeOperation()
()
}
let _ = t.Result
If you don't care for the return value, then you don't have to wait for the task to finish, you can just ignore the whole task:
SomeOperation() |> ignore
If your function returns Async, then simply ignoring it won't work because unlike Tasks, an Async operation does not execute unless you explicitly start it.
If you just want to make it run, but don't care about waiting until it is done you can do
f() |> Async.RunAsTask |> ignore
if you do want to wait for the result, you can do
f() |> Async.RunSynchronously |> ignore

Cancel a sub-block of an F# async workflow

I'm trying to make an async workflow, where there's a main async loop, which executes an async sub-block in each loop. And I want this async sub-block to be cancellable, but when it cancels then I don't want the main loop to cancel. I want it to continue, at the line after the do! subBlock.
The only method I see in Async that even has an acceptable signature (takes CancellationToken, returns something that can be converted to async) is Async.StartAsTask, but that seems to hang when canceled; in the below, it prints "cancelled" and then nothing else.
open System
open System.Threading
open System.Threading.Tasks
// runs until cancelled
let subBlock =
async {
try
while true do
printfn "doing it"
do! Async.Sleep 1000
printfn "did it"
finally
printfn "cancelled!"
}
[<EntryPoint>]
let main argv =
let ctsRef = ref <| new CancellationTokenSource()
let mainBlock =
//calls subBlock in a loop
async {
while true do
ctsRef := new CancellationTokenSource()
do! Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token)
|> Async.AwaitTask
printfn "restarting"
}
Async.Start mainBlock
//loop to cancel CTS at each keypress
while true do
Console.ReadLine() |> ignore
(!ctsRef).Cancel()
0
Is there any way to do this?
Whether the caller that starts and cancels the worker is an async too doesn't really affect this problem, since the worker is managed via its explicitly specified cancellation token.
Asyncs have three continutations: the normal one, which can return a value, one for exceptions, and one for cancellation. There are multiple ways to add a cancellation continuation to an async, such as Async.OnCancel, Async.TryCancelled, or the general Async.FromContinuations, which includes the exception case. Here's a program that has the desired output:
let rec doBlocks () =
async { printfn "doing it"
do! Async.Sleep 1000
printfn "did it"
do! doBlocks () }
let rec runMain () =
use cts = new CancellationTokenSource()
let worker = Async.TryCancelled(doBlocks (), fun _ -> printfn "Cancelled")
Async.Start(worker, cts.Token)
let k = Console.ReadKey(true)
cts.Cancel()
if k.Key <> ConsoleKey.Q then runMain ()
This works just as well if runMain is an async. In this simple case, you could also just have it print the "cancelled" message itself.
I hope this helps. I don't think there is a general answer to how to structure the program; that depends on the concrete use case.
What happens here is that when your child task is cancelled, the OperationCanceledException brings down your mainBlock as well. I was able to get it to work by using this:
let rec mainBlock =
async {
ctsRef := new CancellationTokenSource()
let task = Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token) |> Async.AwaitTask
do! Async.TryCancelled(task, fun e ->
(!ctsRef).Dispose()
printfn "restarting"
Async.Start mainBlock)
}
When the task is cancelled, mainBlock is explicitly restarted in the cancelation handler. You need to add #nowarn "40" for it since mainBlock is used inside its definition. Also note the dispose on token source.
You can find more information on this problem (and perhaps a nicer solution in the form of StartCatchCancellation) in these two threads.

F# async ; Run asynch expression in same thread, and yet be able to wait on async operations (e.g. do!)

Experimenting some with F# async taught me that I can StartImmediate on the current running thread. This allows me, it seems, to run an async expression which can still pass out control, whenever getting inside of it to some async operation (e.g. do!), to the code outside of the async expression. Please see the simple example below:
open System.Threading
let workThenWait() = async {
printfn "async start"
do! Async.Sleep(1000)
printfn "async end"
}
let demo() =
workThenWait() |> Async.StartImmediate
printfn "main started"
// here I want to wait to the async expression in case it has passed control
printfn "main end"
demo()
The result we get is:
async start
main started
main end
async end
On the other hand, if I execute the same async expression (in this case workThenWait) using StartAsTask (inside demo), I can potentially wait on it at the end.
MY QUESTION is:
using the previous example using StartImmediate, can I run on the same thread, but ALSO wait at the end for the async expression in case some async operation (such as do!) is called and passes control forward?
I think You need Async.RunSynchronously (http://msdn.microsoft.com/en-us/library/ee370262.aspx)
update:
Ok, now I understand better what You want, and I was able to achieve this with Async.StartWithContinuations method.
Here's the code:
open System.Threading
let f() =
printfn "main thread: %A" Thread.CurrentThread.ManagedThreadId
let c1 =
async {
printfn "c1 async thread: %A" Thread.CurrentThread.ManagedThreadId
do! Async.Sleep(1000)
return "some result"
}
let continuation s =
printfn "continuation thread: %A" Thread.CurrentThread.ManagedThreadId
printfn "now the code You want after waiting and the result %s" s
Async.StartWithContinuations(
c1,
continuation,
(fun _ -> ()),
(fun _ -> ())
)
printfn "Code that runs during async computation"
Now this is definitely not very readable as the flow of the code is not obvious. I couldn't find any better solution.
You can do this with Hopac libary:
let workThenWait() = job {
printfn "async start"
do! Hopac.Timer.Global.sleep (TimeSpan.FromMilliseconds 1000.)
printfn "async end"
}
let demo() =
let promise = workThenWait() |> Promise.start |> run
printfn "main started"
// here I want to wait to the async expression in case it has passed control
let result = run promise
printfn "main end"
demo()
Hopac is both more performant and functional than async and is little known compared to how good it is. I highly recommend it.

Inconsistent behaviour when cancelling different kinds of Asyncs

I have problems with seemingly inconsistent behavior when cancelling different kinds of Asyncs.
To reproduce the problem, let's says there is a function that takes a list of "jobs" (Async<_> list), waits for them to complete and prints their results. The function also gets a cancellation token so it can be cancelled:
let processJobs jobs cancel =
Async.Start(async {
try
let! results = jobs |> Async.Parallel
printfn "%A" results
finally
printfn "stopped"
}, cancel)
The function is called like that:
let jobs = [job1(); job2(); job3(); job4(); job5()]
use cancel = new CancellationTokenSource()
processJobs jobs cancel.Token
And somewhat later it is cancelled:
Thread.Sleep(1000)
printfn "cancelling..."
cancel.Cancel()
When the cancellation token source is cancelled, the function should execute the finally-block and print "stopped".
That works fine for job1, 2 and 3, but doesn't work when there is a job4 or job5 in the list.
Job1 just Async.Sleeps:
let job1() = async {
do! Async.Sleep 1000000
return 10
}
Job2 starts some async childs and waits for them:
let job2() = async {
let! child1 = Async.StartChild(async {
do! Async.Sleep 1000000
return 10
})
let! child2 = Async.StartChild(async {
do! Async.Sleep 1000000
return 10
})
let! results = [child1; child2] |> Async.Parallel
return results |> Seq.sum
}
Job3 waits for some ugly wait handle that's set by some even uglier thread:
let job3() = async {
use doneevent = new ManualResetEvent(false)
let thread = Thread(fun () -> Thread.Sleep(1000000); doneevent.Set() |> ignore)
thread.Start()
do! Async.AwaitWaitHandle(doneevent :> WaitHandle) |> Async.Ignore
return 30
}
Job4 posts to and waits for a reply from a MailboxProcessor:
let job4() = async {
let worker = MailboxProcessor.Start(fun inbox -> async {
let! (msg:AsyncReplyChannel<int>) = inbox.Receive()
do! Async.Sleep 1000000
msg.Reply 40
})
return! worker.PostAndAsyncReply (fun reply -> reply) // <- cannot cancel this
}
Job5 waits for a Task (or TaskCompletionSource):
let job5() = async {
let tcs = TaskCompletionSource<int>()
Async.Start(async {
do! Async.Sleep 1000000
tcs.SetResult 50
})
return! (Async.AwaitTask tcs.Task) // <- cannot cancel this
}
Why can Job1, 2 and 3 be cancelled ("stopped" gets printed), while Job4 and 5 make the function hang "forever"?
So far I always relied on F# to handle cancellation behind the scenes - as long as I'm in async-blocks and use !s (let!, do!, return!,...) everything should be fine.. but that doesn't seem to be the case all the time.
Quote:
In F# asynchronous workflows, the CancellationToken object is passed
around automatically under the cover. This means that we don't have to
do anything special to support cancellation. When running asynchronous
workflow, we can give it cancellation token and everything will work
automatically.
Complete code is available here: http://codepad.org/euVO3xgP
EDIT
I noticed that piping an async through Async.StartAsTask followed by Async.AwaitTask makes it cancelable in all cases.
i.e. for Job4 that means changing the line:
return! worker.PostAndAsyncReply (fun reply -> reply)
to:
return! cancelable <| worker.PostAndAsyncReply (fun reply -> reply)
With cancelable being:
let cancelable (x:Async<_>) = async {
let! cancel = Async.CancellationToken
return! Async.StartAsTask(x, cancellationToken = cancel) |> Async.AwaitTask
}
The same works for making Job5 cancelable.
But.. that's just a workaround and I can hardly put that around each call to an unknown Async<_>.
Only the Async. methods handle using the default CancellationToken themselves.
In your MailboxProcessor example the cancel should go on the Start method
let! ct= Async.CancellationToken
use worker := MailboxProcessor.Start( theWork, ct)
In the TaskCompletionSource example, you are going to have to register a callback to cancel it.
let! ct = Async.CancellationToken
use canceler = ct.Register( fun () -> tcs.TrySetCanceled() |> ignore )

Global state and Async Workflows in F#

A common example used to illustrate asynchronous workflows in F# is retrieving multiple webpages in parallel. One such example is given at: http://en.wikibooks.org/wiki/F_Sharp_Programming/Async_Workflows Code shown here in case the link changes in the future:
open System.Text.RegularExpressions
open System.Net
let download url =
let webclient = new System.Net.WebClient()
webclient.DownloadString(url : string)
let extractLinks html = Regex.Matches(html, #"http://\S+")
let downloadAndExtractLinks url =
let links = (url |> download |> extractLinks)
url, links.Count
let urls =
[#"http://www.craigslist.com/";
#"http://www.msn.com/";
#"http://en.wikibooks.org/wiki/Main_Page";
#"http://www.wordpress.com/";
#"http://news.google.com/";]
let pmap f l =
seq { for a in l -> async { return f a } }
|> Async.Parallel
|> Async.Run
let testSynchronous() = List.map downloadAndExtractLinks urls
let testAsynchronous() = pmap downloadAndExtractLinks urls
let time msg f =
let stopwatch = System.Diagnostics.Stopwatch.StartNew()
let temp = f()
stopwatch.Stop()
printfn "(%f ms) %s: %A" stopwatch.Elapsed.TotalMilliseconds msg temp
let main() =
printfn "Start..."
time "Synchronous" testSynchronous
time "Asynchronous" testAsynchronous
printfn "Done."
main()
What I would like to know is how one should handle changes in global state such as loss of a network connection? Is there an elegant way to do this?
One could check the state of the network prior to making the Async.Parallel call, but the state could change during execution. Assuming what one wanted to do was pause execution until the network was available again rather than fail, is there a functional way to do this?
First of all, there is one issue with the example - it uses Async.Parallel to run multiple operations in parallel but the operations themselves are not implemented as asynchronous, so this will not avoid blocking excessive number of threads in the thread pool.
Asynchronous. To make the code fully asynchronous, the download and downloadAndExtractLinks functions should be asynchronous too, so that you can use AsyncDownloadString of the WebClient:
let asyncDownload url = async {
let webclient = new System.Net.WebClient()
return! webclient.AsyncDownloadString(System.Uri(url : string)) }
let asyncDownloadAndExtractLinks url = async {
let! html = asyncDownload url
let links = extractLinks html
return url, links.Count }
let pmap f l =
seq { for a in l -> async { return! f a } }
|> Async.Parallel
|> Async.RunSynchronously
Retrying. Now, to answer the question - there is no built-in mechanism for handling of errors such as network failure, so you will need to implement this logic yourself. What is the right approach depends on your situation. One common approach is to retry the operation certain number of times and throw the exception only if it does not succeed e.g. 10 times. You can write this as a primitive that takes other asynchronous workflow:
let rec asyncRetry times op = async {
try
return! op
with e ->
if times <= 1 then return (reraise e)
else return! asyncRetry (times - 1) op }
Then you can change the main function to build a workflow that retries the download 10 times:
let testAsynchronous() =
pmap (asyncRetry 10 downloadAndExtractLinks) urls
Shared state. Another problem is that Async.Parallel will only return once all the downloads have completed (if there is one faulty web site, you will have to wait). If you want to show the results as they come back, you will need something more sophisticated.
One nice way to do this is to use F# agent - create an agent that stores the results obtained so far and can handle two messages - one that adds new result and another that returns the current state. Then you can start multiple async tasks that will send the result to the agent and, in a separate async workflow, you can use polling to check the current status (and e.g. update the user interface).
I wrote a MSDN series about agents and also two articles for developerFusion that have a plenty of code samples with F# agents.

Resources