2014-12-30 23 views
0

假設我有一個異步方法...如何以功能流暢的風格同步調用任務?

public async string MyAsyncMethod() { return await AnyLongRunOperation(); } 

並有我有一個通用的方法來調用同步異步方法:

public static TResult CallSynchronously<TResult>(Func<Task<TResult>> Operation) 
{ 
    var CallingTask = Task.Run(() => Operation()); 
    CallingTask.Wait(); 

    return CallingTask.Result; 
} 

接着,電話可以作出(工作正常) :

string ResultMessage = CallSynchronously(() => MyAsyncMethod()); 

但是,如果我想這種方法轉換成一個擴展方法來調用它在功能流利式(這樣可以避免通過一個lambda一s參數),然後我tryed這... ...

public static TResult CallSynchronously<TResult>(this Task<TResult> Operation) 
{ 
    var CallingTask = Task.Run(() => Operation.Result); 
    CallingTask.Wait(); 

    return CallingTask.Result; 
} 

所以,現在我可以把它像...

var Result = MyAsyncMethod().CallSynchronously(); 

但這裏的問題是:它掛!

所以,問題是:如何以功能流暢風格同步調用任務?

回答

1

正如弗蘭克所說,你可能會看到一個死鎖,我describe in full on my blog。這些代碼塊之間的差:

string ResultMessage = CallSynchronously(() => MyAsyncMethod()); 

和:

var Result = MyAsyncMethod().CallSynchronously(); 

是其中MyAsyncMethod被調用的上下文。在第一個中,它在線程池上下文中調用(在Task.Run之內)。在第二個中,它是直接調用的。推測這是在UI線程中,因此MyAsyncMethod中的任何await都將捕獲UI上下文並嘗試在該上下文中恢復該方法。同時,CallSynchronously正在阻塞UI線程,導致死鎖。

真正的問題是在這裏:

我有一個通用的方法來調用異步方法同步

有極爲罕見的情況下是可以接受的同步調用異步方法(順便說一句,有是用例我可以想到在UI線程上做這個)。這當然不應該是常見的情況。它應該肯定不足以有一個實用的方法只是爲了這個目的。只是這種方法的存在表明應用程序的設計存在嚴重問題。

,而不是試圖破解一起同步過異步代碼,只是擁抱異步:

var Result = await MyAsyncMethod(); 
+0

我給你一個例子:你有一箇中間的Web服務,它依賴於其他Web服務來計算結果。即:不同步並不總是可能的。 –

+0

感謝您的解釋,但問題不是'它爲什麼'掛起來,而是'如何'流利地調用它(功能風格)而不掛。 –

+1

@NéstorSánchezA.:在你的用例中,使中間Web服務異步。除非第一次包裝它(例如'Fluent(MyAsyncMethod).CallSynchronously()'),否則沒有辦法這樣流利。 –