我正在尝试将一些TcpClient
依赖的代码移植到。net 4.5,使用StreamSocket
和DataReader
代替。
我有一个名为ReadLine()
的函数,它在任何地方使用。通过在代码体(LoadAsync()
)中使用DataReader
,我的方法被强制标记为async
关键字。
链式反应如下:现在我有数百个地方,我必须将async
添加到调用方法中,并将await应用于底层async
方法调用。
ReadLine()
,使调用方法不知道它是一个异步方法,这样我就不必改变我的代码的其余部分?也……我经常在多个地方的循环中使用这个方法调用。如果这些方法现在被标记为async
,我担心我可能会在我不应该的时候从流中读取数据,这将导致各种噩梦。这是个问题还是我想得太超前了?
也……我经常在多个地方的循环中使用这个方法调用。如果这些方法现在被标记为异步,我担心我可能会在不应该从流中读取数据的时候读取数据,这将导致各种各样的噩梦。这是个问题还是我想得太超前了?
如果你总是在调用*Async
方法时使用await
,那么你的async
方法就会像同步方法一样(除了它们不会阻塞)。因此,在循环中使用await
将像您期望的那样工作。
async
确实通过代码库"增长"。我通常认为这类似于"海龟一路向下"的古老故事;还有人称它为"僵尸病毒"。
async
增长。
如果必须为异步方法创建同步包装器,请参阅Stephen Toub的建议。您可以使用Task.Result
,但是您需要做两件事:
- 所有地方都使用
ConfigureAwait(false)
。这将避免死锁的情况。 - 请注意
Result
有不同的错误处理语义。
对于您的特定示例,像这样的内容应该足够了:
private async Task<string> ReadLineAsync()
{
... // *Every* await in this method and every method it calls
// must make use of ConfigureAwait(false).
}
public string ReadLine()
{
try
{
return ReadLineAsync().Result;
}
catch (AggregateException ex)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
throw;
}
}
在选择混合同步/异步代码库之前,请仔细考虑复杂性。这并不像看上去那么容易。
注:一般来说,TCP/IP代码应该都是异步的。在套接字上进行连续的异步读取通常是个好主意。
创建一个ReadLineAsync函数和一个ReadLine函数
在ReadLine函数中,你可以像这样调用ReadLineAsync:
var readLineTask = ReadLineAsync();
readLineTask.Wait()
标记为async并不意味着无论何时调用它都必须等待。你将其标记为异步,因为你在方法中使用了await。
这意味着你不必因为在那里调用ReadLine()
而将其他方法标记为async。
如果你的readline方法是可等待的,它必须返回Task
或Task<T>
。