Последние пару дней я пытался выучить F # и продолжаю сталкиваться с тем, что меня озадачивает. Мой "учебный проект" - это скребок экрана для некоторых данных, которыми я немного интересуюсь для манипулирования.
В F # PowerPack происходит вызов Stream.AsyncReadToEnd. Я не хотел использовать PowerPack только для одного звонка, поэтому я посмотрел, как они это сделали.
module Downloader =
open System
open System.IO
open System.Net
open System.Collections
type public BulkDownload(uriList : IEnumerable) =
member this.UriList with get() = uriList
member this.ParalellDownload() =
let Download (uri : Uri) = async {
let UnblockViaNewThread f = async {
do! Async.SwitchToNewThread()
let res = f()
do! Async.SwitchToThreadPool()
return res }
let request = HttpWebRequest.Create(uri)
let! response = request.AsyncGetResponse()
use responseStream = response.GetResponseStream()
use reader = new StreamReader(responseStream)
let! contents = UnblockViaNewThread (fun() -> reader.ReadToEnd())
return uri, contents.ToString().Length }
this.UriList
|> Seq.cast
|> Seq.map Download
|> Async.Parallel
|> Async.RunSynchronously
У них есть эта функция UnblockViaNewThread. Это действительно единственный способ асинхронного чтения потока ответа? Не создает ли новый поток действительно дорого (я видел "~ 1 Мб памяти", разбросанные повсюду). Есть лучший способ сделать это? Это то, что действительно происходит в каждом Async*
звонке (тот, который я могу let!
)?
РЕДАКТИРОВАТЬ: Я следую советам Томаса и на самом деле придумал что-то независимое от F # PowerTools. Вот. Это действительно требует обработки ошибок, но оно асинхронно запрашивает и загружает URL в байтовый массив.
namespace Downloader
open System
open System.IO
open System.Net
open System.Collections
type public BulkDownload(uriList : IEnumerable) =
member this.UriList with get() = uriList
member this.ParalellDownload() =
let Download (uri : Uri) = async {
let processStreamAsync (stream : Stream) = async {
let outputStream = new MemoryStream()
let buffer = Array.zeroCreate<byte> 0x1000
let completed = ref false
while not (!completed) do
let! bytesRead = stream.AsyncRead(buffer, 0, 0x1000)
if bytesRead = 0 then
completed := true
else
outputStream.Write(buffer, 0, bytesRead)
stream.Close()
return outputStream.ToArray() }
let request = HttpWebRequest.Create(uri)
let! response = request.AsyncGetResponse()
use responseStream = response.GetResponseStream()
let! contents = processStreamAsync responseStream
return uri, contents.Length }
this.UriList
|> Seq.cast
|> Seq.map Download
|> Async.Parallel
|> Async.RunSynchronously
override this.ToString() = String.Join(", ", this.UriList)