如何使此功能正确异步?

时间:2014-01-15 02:59:43

标签: f#

我正试图让F#异步工作,而我无法弄清楚我做错了什么。这是我运行的sorta同步代码:

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)

我当时认为要异步运行它会起作用,但事实并非如此:

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.Serialization foo.fs&amp;&amp; ./foo.exe F#   编译器为F#3.1(开源版)自由发布的   Apache 2.0开源许可证

     

/home/frew/code/foo.fs(19,18):错误FS0001:这个表达式是   预计有类型       异步&LT;'一&GT;但这里有类型       单位

     

/home/frew/code/foo.fs(20,17):错误FS0041:一个独特的重载   方法'ReadObject'无法根据类型信息确定   在此计划点之前。可能需要类型注释。   候选人:XmlObjectSerializer.ReadObject(读者:   System.Xml.XmlDictionaryReader):obj,   XmlObjectSerializer.ReadObject(reader:System.Xml.XmlReader):obj,   XmlObjectSerializer.ReadObject(stream:System.IO.Stream):obj

     

/home/frew/code/foo.fs(20,17):错误FS0008:此运行时强制或   从类型进行类型测试       '到       人员涉及基于此计划点之前的信息的不确定类型。不允许运行时类型测试   一些类型。需要进一步的类型注释。

我在这里缺少什么?

1 个答案:

答案 0 :(得分:2)

OpenReadAsync是.NET BCL的一部分,因此在设计时不考虑F#async。您会注意到它返回unit,而不是Async<Stream>,因此它不会与let一起使用!。

API旨在与事件一起使用(即您必须连接client.OpenReadCompleted)。

你有几个选择。

  1. FSharp.Core中有一些很好的辅助方法可以提供帮助 您将API转换为更友好的F#(请参阅 Async.AwaitEvent)。
  2. 使用AsyncDownloadString,这是可以在Microsoft.FSharp.Control.WebExtensions中找到的WebClient扩展方法。这比较容易,所以我在下面做了,虽然它确实意味着将整个流作为字符串保存在内存中,所以如果你有大量的Json,这可能不是最好的主意。
  3. 使用async代替并行运行的任务也是更惯用的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[]>