2017-06-13 22 views
1

嘗試瞭解流和async/await。如果我undertand正確await返回執行調用者,所以我希望下面的代碼的結果是:await不會按預期的方式返回給調用方使用StreamReader.ReadToEndAsync()

before calling async ReadToEnd 
after the call to ReadToEnd 
before calling async ReadToEnd 
after the call to ReadToEnd 

但insted的是

before calling async ReadToEnd 
after the call to ReadToEnd 
before calling async ReadToEnd 
after await in ReadToEnd. streamReader.BaseStream.GetType(): System.IO.MemoryStream 
after the call to ReadToEnd 

看來,使用StreamReaderFileStream下撥打StreamReader.ReadToEndAsync()時確實返回給主叫方,但在使用底下的MemoryStream時使用StreamReader時不起作用。

試圖瞭解是怎麼回事,我就BaseStream閱讀.NET源代碼,並來到conculssion,調用StreamReader.ReadToEndAsync()MemoryStream下最終調用ReadAsync()source)。在MemoryStream的情況下ReadAsync()只是不是一個真正的異步方法,因此不會返回給調用者。

我的理解是否正確?

using System; 
using System.Text; 
using System.Linq; 
using System.IO; 
using System.Threading.Tasks; 

static class Program 
{ 
    public static void Main(string[] args) 
    { 
     var longString = new string(Enumerable.Repeat('x', 100000000).ToArray()); 
     File.WriteAllText("bigFile.txt", longString, Encoding.UTF32); 

     StreamReader fileStreamReader = new StreamReader(File.OpenRead(@"bigFile.txt")); 
     Console.WriteLine("before calling async ReadToEnd"); 
     var task = ReadToEnd(fileStreamReader); 
     Console.WriteLine("after the call to ReadToEnd"); 

     byte[] bytes = Encoding.UTF32.GetBytes(longString); 
     StreamReader memoryStreamReader = new StreamReader(new MemoryStream(bytes)); 
     Console.WriteLine("before calling async ReadToEnd"); 
     var task2 = ReadToEnd(memoryStreamReader); 
     Console.WriteLine("after the call to ReadToEnd"); 

     fileStreamReader.Dispose(); 
     memoryStreamReader.Dispose(); 
    } 

    static async Task ReadToEnd(StreamReader streamReader) 
    { 
     string allText = await streamReader.ReadToEndAsync(); 
     Console.WriteLine("after await in ReadToEnd. streamReader.BaseStream.GetType(): " + streamReader.BaseStream.GetType()); 
    } 
} 
+0

我認爲這樣做,但MemoryStream足夠快,立即返回。因此,您會看到額外的Console.WriteLine出現在您期望的之前。在await和Console之間添加一個Sleep。WriteLine,看看會發生什麼...... – AroglDarthu

+1

@AroglDarthu它實際上並不是一個「足夠快」的問題 - 它只是*不是異步*;一種方法可以是以下任何一種:慢速和同步,慢速和異步,快速和同步,快速和異步。 Fast vs Slow是沒有實際意義的 - 它只是同步vs異步纔有意義(在合理的範圍內;顯然,如果它是異步的並且足夠快以至於能夠擊敗ret指令,那麼......好吧,也許;但是這是一個邊緣案例這沒關係) –

+0

@MarcGravell感謝您的洞察力。 – AroglDarthu

回答

4

await只有返回執行調用者,如果事情被期待已久的沒有同步完成。

  • 他們已經知道了答案(緩存的結果,緩衝的數據,已知故障狀態等)
  • 他們沒有合適的異步:是的,async方法可以,如果他們選擇,要麼是因爲同步完成下游操作在所有

MemoryStream執行是總是同步,所以它總是執行此操作。

FieStream,相比之下,可能會或可能不會同步完成,這取決於數據是可用緩衝器等

是否返回給調用者被GetAwaiter().IsCompleted,驅動決定其(對於Task)歸結爲.IsCompleted

2

在你的代碼是從File創建StreamReader的第一部分。當您致電streamReader.ReadToEndAsync()時,必須首先將文件從硬盤驅動器讀到內存中,然後才能返回異步完成的內容。

在代碼的第二部分中,您將從MemoryStream創建StreamReader,該文件是從bytes構建的,已經裝載到應用程序的內存中。在這種情況下,streamReader.ReadToEndAsync()會立即返回內容,因爲數據在內存中,並且可以立即提取。

相關問題