2014-01-15 55 views
-1

我試圖讓F#異步工作,我只是無法弄清楚我做錯了什麼。這裏是我的運行八九不離十syncronous代碼:如何使此功能正確異步?

open System.Net 
open System.Runtime.Serialization 
open System.Threading.Tasks 

[<DataContract>] 
type Person = { 
    [<field: DataMember(Name = "name")>] 
    Name : string 
    [<field: DataMember(Name = "phone")>] 
    Phone : int 
} 

let url = "http://localhost:5000/app/plugins/anon/CCure" 
let js = Json.DataContractJsonSerializer(typeof<Person>) 

let main x = 
    let client = new WebClient() 
    let url = url + "/" + x 
    let reader = client.OpenRead(url) 
    let person = js.ReadObject(reader) :?> Person 

    printfn "Name: %s, Phone number: %d" person.Name person.Phone 

printfn "starting x" 
let x = Task.Factory.StartNew(fun() -> main "x") 
printfn "starting y" 
let y = Task.Factory.StartNew(fun() -> main "y") 
Task.WaitAll(x, y) 

我想,運行它asyncronously這會工作,但不會:

open System.Net 
open System.Runtime.Serialization 
open System.Threading.Tasks 

[<DataContract>] 
type Person = { 
    [<field: DataMember(Name = "name")>] 
    Name : string 
    [<field: DataMember(Name = "phone")>] 
    Phone : int 
} 

let url = "http://localhost:5000/app/plugins/anon/CCure" 
let js = Json.DataContractJsonSerializer(typeof<Person>) 

let main x = async { 
    let client = new WebClient() 
    let url = url + "/" + x 
    let! reader = client.OpenReadAsync(url) 
    let person = js.ReadObject(reader) :?> Person 

    printfn "Name: %s, Phone number: %d" person.Name person.Phone } 

printfn "starting x" 
let x = Task.Factory.StartNew(fun() -> main "x") 
printfn "starting y" 
let y = Task.Factory.StartNew(fun() -> main "y") 
Task.WaitAll(x, y) 

$ fsharpc -r System.Runtime。系列化foo.fs & & ./foo.exe F# 編譯器F#3.1(開源版)的 Apache 2.0的開源許可下可自由分佈

/home/frew/code/foo.fs(19,18):錯誤FS0001的: 預期該表達式爲具有類型 異步<「一>但這裏有類型 單元

/家/弗魯/代碼/ foo.fs(20,17):錯誤FS0041:無法根據此程序點之前的類型信息 確定方法'ReadObject'的唯一過載 。可能需要類型註釋。 候選人:XmlObjectSerializer.ReadObject(讀取器: System.Xml.XmlDictionaryReader):OBJ, XmlObjectSerializer.ReadObject(讀取器:System.Xml.XmlReader):OBJ, XmlObjectSerializer.ReadObject(流:System.IO.Stream):OBJ

/home/frew/code/foo.fs(20,17):錯誤FS0008:此運行時強制或從 「型 類型測試一個到 人涉及基於之前該程序點信息的不確定的類型。運行時類型測試不允許在 某些類型。需要進一步的類型註釋。

我在這裏錯過了什麼?

+0

我的猜測是主要異步塊中的'printfn'語句。也許增加'人'作爲回報​​會有所幫助。 – leppie

回答

2

OpenReadAsync是.NET BCL的一部分,因此沒有考慮到F#異步的設計。你會注意到它返回unit,而不是Async<Stream>,所以它不會與let!一起使用。

API旨在與事件一起使用(即,您必須連接client.OpenReadCompleted)。

這裏有幾個選項。

  1. 有在FSharp.Core一些不錯的輔助方法,可以幫助你 的API轉換成更F#友好之一(見 Async.AwaitEvent)。
  2. 使用AsyncDownloadString,WebClient的擴展方法,可在Microsoft.FSharp.Control.WebExtensions中找到。這很容易,所以我在下面做了,儘管它意味着將整個流保存在內存中作爲一個字符串,所以如果你有大量的Json,這可能不是最好的主意。

這也是更習慣的F#使用異步,而不是並行運行的任務。

open System.Net 
open System.Runtime.Serialization 
open System.Threading.Tasks 
open Microsoft.FSharp.Control.WebExtensions 
open System.Runtime.Serialization.Json 

[<DataContract>] 
type Person = { 
    [<field: DataMember(Name = "name")>] 
    Name : string 
    [<field: DataMember(Name = "phone")>] 
    Phone : int 
} 

let url = "http://localhost:5000/app/plugins/anon/CCure" 
let js = Json.DataContractJsonSerializer(typeof<Person>) 


let main x = async { 
    printfn "Starting %s" x 
    let client = new WebClient() 
    let url = url + "/" + x 
    let! json = client.AsyncDownloadString(System.Uri(url)) 
    let bytes = System.Text.Encoding.UTF8.GetBytes(json) 
    let st = new System.IO.MemoryStream(bytes) 
    let person = js.ReadObject(st) :?> Person 

    printfn "Name: %s, Phone number: %d" person.Name person.Phone } 


let x = main "x" 
let y = main "y" 

[x;y] |> Async.Parallel |> Async.RunSynchronously |> ignore<unit[]> 
+0

這裏有東西似乎不正確...我從它得到這個異常:System.IO.FileNotFoundException:找不到文件「/ home/frew/code/fsharp/{」name「:」john「,」phone「: 12312312}」。 –