Task.FromAsync和两个线程

时间:2015-10-23 14:01:26

标签: c# multithreading asynchronous task-parallel-library

我正在使用.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中使用的。

提前致谢。

1 个答案:

答案 0 :(得分:2)

你这样做是错的,我几乎不倾向于回答 - 你会伤害那些拥有该代码的人。但由于这不是Code Review ...

您的大多数直接问题是您提供给BeginRead的回调不属于IAsyncResult 的所有。因此,when a specified IAsyncResult completes不会谈论你的回调,它只讨论底层的异步操作 - 你会得到同一事件发起的两个单独的回调。

现在,对于其他问题:

  • 您需要一遍又一遍地发出BeginRead,直到EndRead返回0.否则,您最多只读取整个缓冲区 - 如果您的文件长于此值,你不打算读完整个文件。
  • 您正在将旧式异步API回调与基于任务的异步相结合。这肯定会给你带来麻烦。只要学会正确使用任务,你就会发现回调是100%不必要的。
  • 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
  }
}