none
Questions about WriteAsync? RRS feed

  • Question

  • WriteAsync is asynchronous, but I use the following code test, why WriteAsync will block it?

    I did not use the await keyword, zs2 should be immediately output, why obstruction for a while before output?

     static void Main(string[] args)
            {
             
                ProcessWrite();
                Console.WriteLine("zs2");
                Console.ReadLine();
            }
    
            public static async void ProcessWrite()
            {
                string filePath = @"temp2.txt";
                StringBuilder text = new StringBuilder();
                for (int i = 0; i < 10000000; i++)
                {
                    text.Append("Hello World");
                }
    
                Console.WriteLine("zs");
                WriteTextAsync(filePath, text.ToString());
            }
    
            private static async Task WriteTextAsync(string filePath, string text)
            {
                byte[] encodedText = Encoding.Unicode.GetBytes(text);
    
                using (FileStream sourceStream = new FileStream(filePath,
                    FileMode.Append, FileAccess.Write, FileShare.None,
                    bufferSize: 4096, useAsync: true))
                {
                    sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
                };
            }  


    please verify my account


    • Edited by lctk Friday, December 1, 2017 5:07 AM
    Friday, December 1, 2017 5:06 AM

Answers

  • I think the behavior you're seeing is because a task doesn't necessarily have to be run on a secondary thread. It is up to the scheduler to decide whether a task should be moved or not. In some cases it won't be. That is what the IAsyncResult.CompletedAsychronously indicates. 

    But you're dealing with a file stream so you'd have to go digging through the code. For the most part the OS already buffer writes data to disk so the time it takes to copy data from your app to the OS buffer is not very large. Taking a quick scan of the code it looks like there are several places where the async method will simply return completed tasks because it does the bulk of the work on the calling thread. Async doesn't mean the entire implementation is on a secondary thread, only the portions the method implementation decides to put there. In the case of file stream it looks like the memory copy isn't on the secondary thread. But that is just a guess looking at the current code.


    Michael Taylor http://www.michaeltaylorp3.net

    Saturday, December 2, 2017 11:13 PM
    Moderator

All replies

  • How long does it take to append 10,000,000 "Hello World" strings?

    Friday, December 1, 2017 8:23 AM
  • To achieve a different (expected) behaviour, try this:

       Task.Run( () => ProcessWrite() );

       Console.WriteLine( "zs2" );

     

    Also fix the warnings by inserting some missing await.

    According to documentation for async and await, putting async does not mean that the function is started in parallel.

    Friday, December 1, 2017 10:15 AM
  • The documentation says asynchronous is non-blocking, but it blocks the UI thread

    please verify my account

    Friday, December 1, 2017 12:37 PM
  • The documentation says asynchronous is non-blocking, but it blocks the UI thread

    please verify my account

    Friday, December 1, 2017 12:37 PM
  • WriteAsync is a non-blocking call and will return immediately. Since you didn't await on it that means WriteTextAsync will return immediately as well. Since you're not awaiting it, ProcessWrite returns immediately as well. Then your 2 WriteLines execute and the program terminates (potentially before the writes are complete).

    You can verify this behavior by setting a breakpoint on Console.WriteLine in Main. Then set another breakpoint on the WriteTextAsync call. Then another on the WriteAsync call. Then run the code in the debugger. You'll hit the BP in WriteTextAsync first but only after you've run through your for loop building up a large text string and then write out "zs" to console. However long it takes to hit that first BP is all the setup code you had prior to that method call.

    When you continue running it'll hit the next BP on WriteAsync. This make take a little while because you're creating a file and writing text to it. The time it took to get to this BP is the time it took to do that.

    When you continue again you'll see it almost instantly get to the last BP in Main. That is because the WriteAsync has been started but you're not waiting for the results. 

    Note that putting code in an async method does not cause all the code in that method to be non-blocking. Because you're not using await on any of your async calls, the methods run sync. The compiler will report a warning as such for each of the methods. If you were to await those calls then you'd see the method(s) "return" at the point of the await call. Everything before the first await is still running on the calling thread. Everything after the await is running on the same thread unless you also use ConfigureAwait(false).


    Michael Taylor http://www.michaeltaylorp3.net

    Friday, December 1, 2017 4:03 PM
    Moderator
  • I follow the test you said, but the result is not right. You can try:

    class Program
        {
            static void Main(string[] args)
            {
             
                ProcessWrite();
                Console.WriteLine("zs22");
                Console.ReadLine();
            }
    
            public static async void ProcessWrite()
            {
                string filePath = @"temp2.txt";
                StringBuilder text = new StringBuilder();
                for (int i = 0; i < 10000000; i++) {
                    text.Append("Hello World");
                }
                    //Parallel.For(0, 10000000, (i) =>
                    //{
    
                    //    
    
                    //});
               
    
                Console.WriteLine("zs");
                //Console.WriteLine(text.ToString());
                WriteTextAsync(filePath, text.ToString());
            }
    
            private static async Task WriteTextAsync(string filePath, string text)
            {
                byte[] encodedText = Encoding.Unicode.GetBytes(text);
    
                using (FileStream sourceStream = new FileStream(filePath,
                    FileMode.Append, FileAccess.Write, FileShare.None,
                    bufferSize: 4096, useAsync: true))
                {
                    Console.WriteLine("zcccc");
                     sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
                     Console.WriteLine("zs1111");
                };
            }  
        }

    zcccc should be output with zs1111, but not,
    Can you explain the reason?


    please verify my account

    Saturday, December 2, 2017 5:47 AM
  • You must not have debugging it correctly then. I can take your code and I see the behavior I described. Right after the WriteAsync method is called, the zs1111 gets written to output followed by zs22.

    But you do have a possible unhandled exception in your code. Because WriteAsync is running async it will potentially execute after the zs1111 call. But that code is wrapped in a using statement so the stream may get disposed before the WriteAsync finishes. If this occurs then an exception will occur. Normally you'd get this exception when you await on the task or get the result. But since you're not blocking it'll be silently eaten.

    Try this simple change to your WriteTextAsync method to see how the async call isn't stopping anything. You'll get zccc followed by zs1111 almost instantly. There is never a 5 second delay like the task had requested.

    private static async Task WriteTextAsync ( string filePath, string text )
    {
        Console.WriteLine("zcccc");
        //sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
        Task.Delay(5000);
        Console.WriteLine("zs1111");            
    }
    Put an await in front of that Task.Delay and you'll see the 5 second delay before zs1111 shows up. But because Main isn't blocking, you'll actually see zs22 show up before zs1111.


    Michael Taylor http://www.michaeltaylorp3.net

    Saturday, December 2, 2017 6:35 AM
    Moderator
  • Yes, zs1111 output before zs22,
    But did you find out? The behavior of task.delay and WriteAsync is different,

     private static async Task WriteTextAsync(string filePath, string text)
            {
                Console.WriteLine("zcccc");
                //sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
                Task.Delay(100000000);
                Console.WriteLine("zs1111");
            }

    Even if the delay method parameters 100000000, zs1111 is also immediately output,
    But you find that WriteAsync is actually blocked, you will not immediately see the zs1111 output, but after a while before zs1111 output, is not WriteAsync asynchronous non-blocking?


    please verify my account

    Saturday, December 2, 2017 6:47 AM
  • I think the behavior you're seeing is because a task doesn't necessarily have to be run on a secondary thread. It is up to the scheduler to decide whether a task should be moved or not. In some cases it won't be. That is what the IAsyncResult.CompletedAsychronously indicates. 

    But you're dealing with a file stream so you'd have to go digging through the code. For the most part the OS already buffer writes data to disk so the time it takes to copy data from your app to the OS buffer is not very large. Taking a quick scan of the code it looks like there are several places where the async method will simply return completed tasks because it does the bulk of the work on the calling thread. Async doesn't mean the entire implementation is on a secondary thread, only the portions the method implementation decides to put there. In the case of file stream it looks like the memory copy isn't on the secondary thread. But that is just a guess looking at the current code.


    Michael Taylor http://www.michaeltaylorp3.net

    Saturday, December 2, 2017 11:13 PM
    Moderator