Your question is conflating two issues, namely multithreading and asychrony. It's important to realise that these things are entirely different concepts:
Asychrony is about a workflow of tasks where we respond to the completion of those tasks independently of the main program flow.
Multithreading is an execution model, one which can be used to implement asychrony, although asychrony can be acheived in other ways (such as hardware interrupts).
Now, when it comes to I/O, the question you should not be asking is "Can I spin up another thread to do it for me?"
Why, you ask?
If you do some I/O in the main thread, you typically block the main thread waiting for results. If you evade this problem by creating a new thread, you haven't actually solved the issue, you've just moved it around. Now you've blocked either a new thread that you've created or a thread pool thread. Oh dear, same problem.
Threads are an expensive and valuable resources and shouldn't be squandered on waiting for blocking I/O to complete.
So, what is the real solution?
Well, we achieve asynchrony via one of these other approaches. That way, we can request that the OS perform some I/O and request that it let us know when the I/O operation is complete. That way, the thread is not blocked while we're waiting for results. In Windows, this is implemented via something called I/O completion ports.
How do I do this in F#?
The .NET CopyToAsync
method is probably the easiest approach. Since this returns a plain task, it's helpful to create a helper method:
type Async with
static member AwaitPlainTask (task : Task) =
task.ContinueWith(ignore) |> Async.AwaitTask
Then
[<Literal>]
let DEFAULT_BUFFER_SIZE = 4096
let copyToAsync source dest =
async {
use sourceFile = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.Read, DEFAULT_BUFFER_SIZE, true);
use destFile = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, DEFAULT_BUFFER_SIZE, true);
do! sourceFile.CopyToAsync(destFile) |> Async.AwaitPlainTask
}
You could then use this with Async.Parallel
to perform multiple copies concurrently.
Note: This is different to what you wrote above because File.Copy
is a sychronous method that returns unit
while CopyToAsync
is an async method that returns Task
. You cannot magically make synchronous methods asychronous by putting async wrappers around them, instead you need to make sure you are using async all the way down.