2014-04-01 50 views
4

消費F#異步假設我有以下代碼:從C#

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返回異步<'T>。但是,暴露F#特定類型是不好的做法。所以最好的做法是返回一個任務。然而,Async.StartAsTask是有問題的,因爲它會導致代碼在單獨的線程中運行。我想要的是我想返回一個任務,但它也應該像Async.StartImmediate一樣行事。因此代碼的非異步部分應該在原始主線程中運行。 這裏我假設我從UI線程運行它,以便所有調用都將返回相同的線程ID。換句話說,我想要一個Async.StartImmediate,但返回一個任務。 這是可以實現的嗎?

回答

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) 
+0

以下如果我運行這個,我得到這樣的輸出:'X 8,第一個條8,第一個異步8,最後一個異步11',就像在我的(更簡單)的答案。 –

+0

哦,是的,抱歉,您可能正在嘗試從控制檯應用程序,而等待主線程。我從UI線程運行我的代碼,並且所有打印語句都返回相同的ID。你的代碼有調用RunSynchronously的問題。它會創建一個死鎖或在另一個線程中運行last語句 –

+0

+1如果我正確閱讀該問題,這就是我所寫的內容! –

2

您可以使用Async.StartAsTask<'T>方法將Async<'T>轉換爲Task<'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) 
+2

正如我所說,這不會satisify我的問題,導致在運行非異步代碼不同的線程。 –

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()

+0

所有線程ID必須是7.我實際上找到了答案,我將發佈在 –