我一直在尝试实现一个支持ReadAsync
和WriteAsync
的Stream
,并且考虑到文档的稀疏性,我正在努力理解如何正确地这样做。具体来说,与流的游标位置有关。关于旧的BeginRead
函数,这里和这里都提出了类似的问题。该函数的文档似乎表明,在任何挂起的异步操作完成之前,不应该再次调用BeginRead
。
考虑到BeginRead
现在已被弃用不再推荐用于新开发,Stream
可能已经进行了重大更改以实现新的Async函数,事情再次变得不清楚。(编辑:通常这种警告意味着新函数是直接实现的,旧函数调用新函数,只是为了向后兼容而存在,但这似乎不是这里的情况)。
ReadAsync
和WriteAsync
函数的定义是,它们不像Win32对应的函数那样采取所需的读/写流位置(在我看来是一个非常糟糕的设计选择),而是依赖于流实现所持有的当前位置。如果满足以下两个条件之一,这种情况很好:
-
ReadAsync
和WriteAsync
必须在返回Task
或 之前获取当前光标位置以供操作使用,并将其更新为操作完成(或根本不更新)。 - 在所有之前的异步调用完成之前,不能调用
ReadAsync
或WriteAsync
。
在这两个条件之外,调用者永远无法确定读或写将发生在哪个位置,因为挂起的异步操作可能会改变任何Seek
和对ReadAsync
或WriteAsync
的调用之间的流的位置。这两个条件都没有作为需求记录下来,所以我想知道它应该如何发挥作用。
我的白盒测试似乎表明,至少对于Stream
的FileStream
版本,流位置异步更新,这似乎表明第二个条件(只允许一个挂起操作)仍然是必需的,但这似乎是一个严重的限制(它当然排除了任何类型的内部分散-收集实现)。
谁能提供任何权威的信息,关于旧的BeginRead
限制是否仍然适用于ReadAsync
?
谁能提供任何权威的信息,关于旧的
BeginRead
限制是否仍然适用于ReadAsync
?
同样的限制适用于BeginRead
和ReadAsync
。
旧的APM方法没有被弃用。它们仍然得到充分的支持,使用它们没有任何问题。然而,async
方法相当容易使用,所以文档建议使用它们。
所有这些旧类的async
"重载"通常仍然由调用BeginXXX
和EndXXX
组成,或者最多两个选项都调用共享方法(例如FileStream.BeginReadAsync
)。我从未见过任何代码(在框架中或其他地方)在async
之上有APM包装器方法。
因此,调用ReadAsync
将导致调用BeginRead
,因此任何限制都适用于两者。此外,由于Stream
不是线程安全的,也没有宣称它是并发安全的(这略有不同),因此可以安全地假设您不能并发地使用async
请求来淹没它。