4

FileOptions.Asynchronous で FileStream を作成すると、FileStream.BeginRead が呼び出しスレッドをブロックするのはなぜですか?

コード スニペットは次のとおりです。

private static Task<int> ReadFileAsync(string filePath)
  {
     var file = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 64 * 1024, FileOptions.Asynchronous);
     FileInfo fi = new FileInfo(filePath);
     byte[] buffer = new byte[fi.Length];
     Task<int> task = Task<int>.Factory.FromAsync(file.BeginRead, file.EndRead, buffer, 0, buffer.Length, null);         
     return task.ContinueWith(t =>
     {
        file.Close();
        Console.WriteLine("Done ReadFileAsync, read " + t.Result + " bytes.");
        return t.Result;
     });
  }

JetBrains dotPeek を使用して MSFT FileStream コードを掘り下げると、コードにバグがあるようです。

      if (!this._isAsync)
    return base.BeginRead(array, offset, numBytes, userCallback, stateObject);
  else
    return (IAsyncResult) this.BeginReadAsync(array, offset, numBytes, userCallback, stateObject);

実際には、BeginRead メソッドは Task をスケジュールすることで非同期に読み取りを行っているように見えますが、BeginReadAsync メソッドは実際には同期読み取りを行っています。したがって、彼らのメソッド命名命名法は逆であり、どのメソッドが呼び出されるかのロジックは間違っています。this._isAsync == true の場合、BeginRead を呼び出す必要があります。

そのため、FileStream.BeginRead をすぐに返す (非同期的に読み取りをスケジュールする) には、実際には cosntructor の useAsync パラメータを false に設定する必要があるようです。

4

2 に答える 2

4

これは、非同期で実行したいコードを実際に同期して実行するためのすべての方法をリストしたナレッジベースの記事です。

非同期ディスクI/Oは、Windows NT、Windows 2000、およびWindowsXPでは同期として表示されます

リストにあるものはあなたの状況に当てはまりますか?

.NET 4.5のReadAsyncメソッドで同じ動作を実装しようとしましたか?

私はMSDNから引用しています:

.NET Framework 4以前のバージョンでは、BeginReadやEndReadなどのメソッドを使用して非同期I/O操作を実装する必要があります。これらのメソッドは、レガシーコードをサポートするために.NETFramework4.5でも引き続き使用できます。ただし、ReadAsync、WriteAsync、CopyToAsync、FlushAsyncなどの新しい非同期メソッドを使用すると、非同期I/O操作をより簡単に実装できます。

編集 ICH10とWindows7を搭載したOCZVertex2で256MBのファイルを使用して問題を再現しています。ファイルを生成し、PCを再起動してファイルキャッシュをクリアしてから、同じファイルを読み取ってみてください。

using System;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication4
{
   class Program
   {
      static void Main(string[] args)
      {
         string fileName = @"C:\Temp\a1.txt";
         int arraySize = 512 * 1024 * 1024;
         var bytes = new byte[arraySize];
         new Random().NextBytes(bytes);

          // This prints false, as expected for async call
         var callback = new AsyncCallback(result =>
                             Console.WriteLine("Completed Synchronously: " + result.CompletedSynchronously));

         try
         {
            // Use this method to generate file...
            //WriteFileWithRandomBytes(fileName, arraySize, bytes, callback);

            Console.WriteLine("ReadFileAsync invoked at " + DateTimeOffset.Now);
            var task = ReadFileAsync(fileName);
            Console.WriteLine("ReadFileAsync completed at " + DateTimeOffset.Now);

            Task.WaitAll(task);
            Console.WriteLine("Wait on a read task completed at " + DateTimeOffset.Now);
         }
         finally
         {
            if (File.Exists(fileName))
               File.Delete(fileName);
         }
      }

      private static void WriteFileWithRandomBytes(string fileName, int arraySize, byte[] bytes, AsyncCallback callback)
      {
         using (var fileStream = new FileStream(fileName,
            FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 128 * 1024, FileOptions.Asynchronous))
         {
            Console.WriteLine("BeginWrite invoked at " + DateTimeOffset.Now);
            var asyncResult = fileStream.BeginWrite(bytes, 0, arraySize, callback, null);


            Console.WriteLine("BeginWrite completed at " + DateTimeOffset.Now);
            // completes in 6 seconds or so...  Expecting instantaneous return instead of blocking

            // I expect runtime to block here...
            Task.WaitAll(Task.Factory.FromAsync(asyncResult, fileStream.EndWrite));

            // or at least when flushing the stream on the following end-curly
         }
      }


      private static Task<int> ReadFileAsync(string filePath)
      {
         FileInfo fi = new FileInfo(filePath);
         byte[] buffer = new byte[fi.Length];

         var file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None, 64 * 1024, FileOptions.Asynchronous);
         Task<int> task = Task<int>.Factory.FromAsync(file.BeginRead, file.EndRead, buffer, 0, buffer.Length, null);
         return task.ContinueWith(t =>
         {
            file.Close();
            Console.WriteLine("Done ReadFileAsync, read " + t.Result + " bytes.");
            return t.Result;
         });
      }
   }
}

他のすべてが失敗した場合、ここにアンマネージAPIドキュメントへの参照があります

于 2012-11-11T19:34:42.817 に答える
0

null ではなく、有効な stateObject を渡そうとしましたか? http://msdn.microsoft.com/en-us/library/kztecsys(v=vs.100).aspxのコードと比較してください

于 2013-10-22T15:30:14.310 に答える