2014-12-30 22 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()'),否则没有办法这样流利。 –