2012-11-08 88 views
2

爲什麼使用FileOptions.Asynchronous創建FileStream會導致FileStream.BeginRead阻塞調用線程?FileOptions.Asynchronous導致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方法實際上似乎通過安排任務來異步讀取,但BeginReadAsync方法實際上最終會執行同步讀取。 所以他們的方法命名命名是倒退的,並且調用哪個方法的邏輯是錯誤的。如果this._isAsync == true,則應該調用BeginRead。

因此,似乎要讓FileStream.BeginRead立即返回(異步調度讀取),您實際上必須將cosntructor中的useAsync參數設置爲false。

+0

你打算用發佈的代碼片段展示什麼?我無法從中得到任何信息。 – usr

+0

上面的發佈代碼將重現執行線程阻塞在線任務 .Factory.FromAsync(...)直到文件讀取完成的行爲。所以它本質上與我期望的相反,並且以同步方式而不是異步方式讀取文件。這種行爲是什麼讓我感到困惑... – dmg

回答

1

下面是一篇知識庫文章,其中列出了所有可能導致您希望異步執行代碼以實際同步運行的代碼的方法。

Asynchronous Disk I/O Appears as Synchronous on Windows NT, Windows 2000, and Windows XP

是否在名單上的任何適用於您的情況?

您是否嘗試過使用.NET 4.5的ReadAsync method執行相同的行爲?

我從MSDN報價:

在.NET Framework 4個早期版本中,你必須使用方法,如的BeginRead和EndRead實現異步I/O操作。這些方法在.NET Framework 4.5中仍然可用,以支持遺留代碼;但是,新的異步方法(如ReadAsync,WriteAsync,CopyToAsync和FlushAsync)可幫助您更輕鬆地實現異步I/O操作。

編輯我與ICH10和Windows 7我有生成文件,重新啓動計算機以清除文件緩存,然後嘗試和讀取上OCZ的頂點2 256MB的文件再現您的問題相同的文件。

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; 
     }); 
     } 
    } 
} 

當一切都失敗時,here's the reference to unmanaged API documentation

+0

我不知道,如果這適用或不。這很難說。 – dmg

+0

您是否可以使用我發佈的代碼片段在您的機器上重現此行爲? – dmg

+0

@dmg我能夠repro – GregC

相關問題