async all the way down issue

前端 未结 2 1593
面向向阳花
面向向阳花 2021-01-18 13:43

I have an async asp.net controller. This controller calls an async method. The method that actually performs the async IO work is deep down in my application. The series of

2条回答
  •  醉梦人生
    2021-01-18 14:22

    You must be careful about the thread message pumps and what async really does. The sample below calls into an async method which calls two other async methods which start two tasks to do the actual work which wait 2 and 3 seconds.

    13.00 6520 .ctor Calling async method
    13.00 6520 RunSomethingAsync Before
    13.00 6520 GetSlowString Before
    13.00 5628 OtherTask Sleeping for 2s
    15.00 5628 OtherTask Sleeping done
    15.00 6520 GetVerySlow Inside
    15.00 2176 GetVerySlow Sleeping 3s
    18.00 2176 GetVerySlow Sleeping Done
    18.00 6520 RunSomethingAsync After GetSlowOtherTaskResultGetVerySlowReturn
    

    As you can see the calls are serialized which might not be what you want when you after performance. Perhaps the two distinct await calls do not depend on each other and can be started directly as tasks.

    All methods until GetSlowStringBefore are called on the UI or ASP.NET thread that started the async operation (if it it has a message pump). Only the last call with the result of the operation are marshalled back to the initiating thread.

    The performance penalty is somewhere in the ContextSwitch region to wake up an already existing thread. This should be somewhere at microsecond level. The most expensive stuff would be the creation of the managed objects and the garbage collector cleaning up the temporary objects. If you call this in a tight loop you will be GC bound because there is an upper limit how many threads can be created. In that case TPL will buffer your tasks in queues which require memory allocations and then drain the queues with n worker threads from the thread pool.

    On my Core I7 I get an overhead of 2microseconds for each call (comment out the Debug.Print line) and a memory consumption of 6,5GB for 5 million calls in a WPF application which gives you a memory overhead of 130KB per asynchronous operation chain. If you are after high scalability you need to watch after your GC. Until Joe Duffy has finished his new language we have to use CLR we currently have.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Print("Calling async method");
            RunSomethingAsync();
        }
    
        private async void RunSomethingAsync()
        {
            Print("Before");
            string msg = await GetSlowString();
            Print("After " + msg);
            cLabel.Content = msg;
        }
    
        void Print(string message, [CallerMemberName] string method = "")
        {
            Debug.Print("{0:N2} {1} {2} {3}", DateTime.Now.Second, AppDomain.GetCurrentThreadId(), method, message);
        }
    
        private async Task GetSlowString()
        {
            Print("Before");
    
            string otherResult = await OtherTask();
    
            return "GetSlow" + otherResult + await GetVerySlow(); ;
        }
    
        private Task OtherTask()
        {
            return Task.Run(() =>
            {
                Print("Sleeping for 2s");
                Thread.Sleep(2 * 1000);
                Print("Sleeping done");
                return "OtherTaskResult";
            });
        }
    
        private Task GetVerySlow()
        {
            Print("Inside");
            return Task.Run(() =>
            {
                Print("Sleeping 3s");
                Thread.Sleep(3000);
                Print("Sleeping Done");
                return "GetVerySlowReturn";
            });
        }
    }
    

提交回复
热议问题