F# makes it easy to define asynchronous computations using the async builder. You can write an entire program and then pass it to Async.RunSynchronously.
The problem I am having is that some async actions must not be run at the same time; they should be forced to wait for other async actions to complete. This is something like a mutex. However, I do not want to just chain them in serial because this would be inefficient.
Concrete Example: Download Cache
Suppose I want to fetch some remote files using a local file cache. In my application I call fetchFile : Async<string> in many places, but there is a risk that if I call fetchFile on the same URL at the same time, the cache will get corrupted by multiple writes. Instead, the fetchFile command should have behaviour like this:
- If there is no cache, download the file to the cache and then read the cache contents
- If the cache is currently being written to, wait for the write to finish and then read the contents
- If the cache is present and complete, just read the cache contents
fetchFileon two different URLs should work in parallel
I am imagining some kind of stateful DownloadManager class to which requests can be sent and ordered internally.
How do F# programmers usually implement such logic with async?
Imaginary usage:
let dm = new DownloadManager()
let urls = [
"https://www.google.com";
"https://www.google.com";
"https://www.wikipedia.org";
"https://www.google.com";
"https://www.bing.com";
]
let results =
urls
|> Seq.map dm.Download
|> Async.Parallel
|> Async.RunSynchronously
Note: I previously asked this question before about how to run async actions in a semi-parallel fashion, but now I have realized that this approach is hard to compose.
Note: I do not have to worry about multiple instances of the application running at once. In-memory locking is sufficient.