文件流.ReadAsync有时同步完成
本文关键字:同步 ReadAsync 文件 | 更新日期: 2023-09-27 17:50:55
试试吧。设置一个带有单个按钮的新Windows窗体应用程序,并为按钮单击事件设置以下代码:
private async void button1_Click(object sender, EventArgs e)
{
using (var file = File.OpenRead(@"C:'Temp'Sample.txt"))
{
byte[] buffer = new byte[4096];
int threadId = Thread.CurrentThread.ManagedThreadId;
int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
}
}
然后运行应用程序并快速点击按钮。如果你的经验和我一样,你会发现有时它会像预期的那样工作,然而,其他时候Debug.Assert
会失败。
根据我对Stephen Cleary博客上解释的ConfigureAwait
的理解,将false
传递给continueOnCapturedContext
应该指示任务不同步回"主"上下文(在这种情况下是UI线程),并且执行应该继续在线程池上。
为什么断言会随机失败呢?我只能假设ReadAsync
有时不会在后台线程上完成,即它将同步完成。
此行为符合KB 156932 -异步磁盘I/O在Windows上显示为同步:
大多数I/O驱动程序(磁盘、通信和其他)都有特殊情况代码,如果一个I/O请求可以"立即"完成操作将完成,ReadFile或WriteFile函数将返回TRUE。从各个方面来看,这些类型的操作似乎是同步的。对于磁盘设备,I/O请求通常可以是当数据被缓存到内存中时"立即"完成。
我的假设和测试应用是否正确?ReadAsync
方法有时可以同步完成吗?是否有一种方法来保证执行将始终继续在后台线程?
这个问题对我的应用程序造成了严重破坏,它使用COM对象,需要我知道哪个线程一直在执行。
Windows 7 64位,.NET 4.5.1
ReadAsync方法有时可以同步完成吗?
是的。这并不罕见,因为在操作系统级别和。net流类型中都有缓冲区。
是否有一种方法可以保证执行始终在后台线程上继续?
如果您总是希望代码在线程池线程上执行,则使用Task.Run
:
private async void button1_Click(object sender, EventArgs e)
{
using (var file = File.OpenRead(@"C:'Temp'Sample.txt"))
{
byte[] buffer = new byte[4096];
int threadId = Thread.CurrentThread.ManagedThreadId;
int read = await file.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
await Task.Run(() =>
{
Debug.Assert(threadId != Thread.CurrentThread.ManagedThreadId);
}).ConfigureAwait(false);
}
}
ConfigureAwait
是一个优化提示,而不是移动到后台线程的命令。如果操作已经完成,ConfigureAwait
没有作用。在这种情况下,await
遵循"快速路径",这实际上意味着它同步地继续,因此任何ConfigureAwait
提示都将被忽略。
如果文件读取在调用await之前同步完成,我相当确定没有任何异步编译器魔法发生,并且调用线程上的执行继续同步。如果我想保证它在线程池线程上启动,我会使用Task.Run(() => file.ReadAsync(buffer, 0, buffer.Length))
将工作排队到线程池。
当您在Task
上使用await
时,如果任务已经完成,代码将同步执行。然后在该任务上调用ConfigureAwait
将没有什么区别,因为不会涉及其他线程。
听起来好像如果数据在内存中(因为您正在重复读取相同的文件)file.ReadAsync
可以同步完成,因此它将在同一线程上完成。