从C#中消耗F#异步

时间:2014-04-01 11:20:58

标签: f# async-await

假设我有以下代码:

namespace Library1

open System.Threading.Tasks
open System.Threading
open System.Runtime.Remoting.Messaging
open System

type public Class1() =

    let printThread (message) = 
        printfn "%s %A" message Thread.CurrentThread.ManagedThreadId

    let bar = 
        printThread ("first bar")
        async { 
            printThread ("first async")
            do! Async.Sleep(1000)
            printThread "last async"
        }

    member this.X() =  bar

我想使用这个类并从C#调用X.问题是X返回Async<" T>。但是,暴露F#特定类型是不好的做法。所以最好的做法是返回一个Task。但是,Async.StartAsTask有一些问题,因为它会导致代码在一个单独的线程中运行。我想要的是我想要返回一个任务,但它应该表现为Async.StartImmediate。因此,代码的非异步部分应该在原始主线程中运行。 这里我假设我从UI线程运行它,以便所有调用都将返回相同的线程ID。换句话说,我想要一个Async.StartImmediate但是返回一个任务。 这是否可以实现?

3 个答案:

答案 0 :(得分:3)

您可以使用Async<'T>方法将Task<'T>变为Async.StartAsTask<'T>

我通常建议为C#用户简化操作,并使用返回Task<'T>的其他方法扩展F#实现。遵循通常的命名约定,您可以调用F#版本AsyncFoo和C#友好版本FooAsync

看看你的例子,我会选择这样的事情:

type public Class1() =

    let printThread (message) = 
        printfn "%s %A" message Thread.CurrentThread.ManagedThreadId

    let bar = 
        printThread ("first bar")
        async { 
            printThread ("first async")
            do! Async.Sleep(1000)
            printThread "last async"
        }

    member this.AsyncFoo() =  bar

    /// Expose C#-friendly asynchronous method that returns Task
    member this.FooAsync() = Async.StartAsTask(bar)
    /// Expose C#-friendly asynchronous method that returns Task
    /// and takes cancellation token to support cancellation...
    member this.FooAsync(cancellationToken) = 
      Async.StartAsTask(bar, ?cancellationToken=cancellationToken)

答案 1 :(得分:2)

这完全符合我的要求(与此版本返回int的问题不同,这是加号):

type public Class1() = 
    let printThread (message) = printfn "%s %A" message Thread.CurrentThread.ManagedThreadId

    let bar = 
        printThread ("first bar")
        async { 
            printThread ("first async")
            do! Async.Sleep(1000)
            printThread "last async"
            return 1232
         }

    member this.convertToTask<'T> (asyn : Async<'T>) = 
       let tcs1 = new TaskCompletionSource<'T>()
       let t1 = tcs1.Task
       Async.StartWithContinuations
        (
          asyn 
          , (fun (k) -> tcs1.SetResult(k)), (fun exn -> tcs1.SetException(exn)), fun exn -> ())
        t1

    member this.X() : Task<int> =  (bar |> this.convertToTask)

答案 2 :(得分:0)

如此定义X怎么样?

member this.X() = 
    let t = new Task(fun () -> bar |> Async.StartImmediate)
    t.RunSynchronously()
    t

据我所知,它可以满足您的需求。至少,这个C#代码:

Console.WriteLine("X " + Thread.CurrentThread.ManagedThreadId);
var c = new Class1();            
c.X().Wait();

Thread.Sleep(2000);

打印此输出:

X 7
first bar 7
first async 7
last async 11

此处,从C#可以看出,X具有签名Task X()