我正在使用.net 4.0并拥有以下代码:
var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize,
FileOptions.Asynchronous | FileOptions.SequentialScan);
var buffer = new byte[bufferSize];
Debug.Assert(stream.IsAsync, "stream.IsAsync");
var ia = stream.BeginRead(buffer, 0, buffer.Length, t =>
{
var ms = new MemoryStream(buffer);
using (TextReader rdr = new StreamReader(ms, Encoding.ASCII))
{
for (uint iEpoch = 0; item < FileHeader.NUMBER_OF_ITEMS; item++)
{
dataList.Add(epochData);
}
}
}, null);
return Task<int>.Factory.FromAsync(ia, t =>
{
var st = stream;
var bytes1 = st.EndRead(t);
var a = EpochDataList.Count;
var b = FileHeader.NUMBER_OF_EPOCHS;
Debug.Assert(a == b);
st.Dispose();
return bytes1;
});
似乎在执行异步回调和结束方法lambda函数之间存在竞争条件(断言正在提升)。但根据msdn明确指出,在异步回调完成后应该执行end方法:
创建一个在指定的IAsyncResult完成时执行结束方法函数的Task。
我是对的,我混淆了完成IO操作的事实,它触发了最终方法和完成异步回调的事实,所以它们都可以在同一时间执行吗?
同时这段代码很有用:
return Task<int>.Factory.FromAsync(stream.BeginRead, (ai) =>
{
var ms = new MemoryStream(buffer);
using (TextReader rdr = new StreamReader(ms, Encoding.ASCII))
{
using (TextReader rdr = new StreamReader(ms, Encoding.ASCII))
{
for (uint iEpoch = 0; item < FileHeader.NUMBER_OF_ITEMS; item++)
{
dataList.Add(epochData);
}
}
}
stream.Dispose();
return stream.EndRead(ai);
}, buffer, 0, buffer.Length, null);
另外我需要提一下,返回的任务是在continuation中使用的。
提前致谢。
答案 0 :(得分:2)
你这样做是错的,我几乎不倾向于回答 - 你会伤害那些拥有该代码的人。但由于这不是Code Review ...
您的大多数直接问题是您提供给BeginRead
的回调不属于IAsyncResult
的所有。因此,when a specified IAsyncResult completes
不会谈论你的回调,它只讨论底层的异步操作 - 你会得到同一事件发起的两个单独的回调。
现在,对于其他问题:
BeginRead
,直到EndRead
返回0.否则,您最多只读取整个缓冲区 - 如果您的文件长于此值,你不打算读完整个文件。EndRead
告诉你在前面的BeginRead
操作中实际读取了多少字节 - 你忽略了这些信息。正确执行此操作并非易事 - 如果可能,我建议升级到.NET 4.5,并利用await
关键字。如果不可能,您可以安装异步目标包,它将await
添加到4.0作为简单的NuGet包。
使用await
,读取整个文件就像
using (var sr = new StreamReader(fs))
{
string line;
while ((line = await sr.ReadLineAsync(buffer, 0, buffer.Length)) > 0)
{
// Do whatever
}
}