netcore之异步并不是多线程!

1、遇到await,线程的变化

遇到await会把当前线程返回且返回值就是await后面的Task,再从线程池随机取一个线程往下执行代码。

我们使用封装好的异步方法模拟写入大量字符串的耗时操作:

        static async Task Main(string[] args)
        {
            //原线程
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

            //模拟写入大量字符串的耗时操作
            StringBuilder str = new StringBuilder();
            for (int i = 0; i < 1000; i++)
            {
                str.Append("Hello World!");
            }
            await File.WriteAllTextAsync(@"C:\Users\张跑跑\Desktop\net\async\1.txt", str.ToString());

            //异步之后线程
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }

结果显示前后线程不一致

那么问题来了,首先会随机到原来的线程吗?答案是会的,发生在异步操作所需时间很少的情况下。现在,我们仅仅把上述代码的循环次数缩短

        static async Task Main(string[] args)
        {
            ......
          	//降为10次
            for (int i = 0; i < 10; i++)
            {
                str.Append("Hello World!");
            }
          	......
        }

可以看到,线程未改变。(作一个比喻就是,前台(当前线程)放你进店,服务员(新线程)把你领到座位上后忙自己的事,所以你点完菜可能是另一个服务员传,但服务员还没走远,你就点完了菜,那只好她继续上传菜单了)

参考:
https://zhuanlan.zhihu.com/p/343235838
https://www.cnblogs.com/jiangchengbiao/p/10040899.html

2、异步不等于多线程

自定义的异步方法并不会自动在新线程中执行,除非手动new线程、或者委托、或使用封装好的异步方法

我们模拟一个自定义异步方法

        static async Task Main(string[] args)
        {
            //原线程
            Console.WriteLine("原线程:" + Thread.CurrentThread.ManagedThreadId);
            double r = await TestAsync();
            Console.WriteLine(r);
            //异步之后线程
            Console.WriteLine("异步后线程:" + Thread.CurrentThread.ManagedThreadId);
        }


        static async Task<double> TestAsync()
        {
            //异步中线程
            Console.WriteLine("异步后线程:" + Thread.CurrentThread.ManagedThreadId);
            double n = 0;
            Random random = new Random();
            for (int i = 0; i < n * n; i++)
            {
                n = random.NextDouble();
            }
            return n;
        }

结果是线程未改变

现在我们模拟一个委托

        static async Task Main(string[] args)
        {
            //原线程
            Console.WriteLine("原线程:" + Thread.CurrentThread.ManagedThreadId);
            double r = await TestAsync1();
            Console.WriteLine(r);
            //异步之后线程
            Console.WriteLine("异步后线程:" + Thread.CurrentThread.ManagedThreadId);
        }


        static async Task<double> TestAsync()
        {
            return await Task.Run(() =>
            {
                //异步中线程
                Console.WriteLine("异步后线程:" + Thread.CurrentThread.ManagedThreadId);
                double n = 0;
                Random random = new Random();
                for (int i = 0; i < n*n; i++)
                {
                    n = random.NextDouble() ;
                }
                return n;
            });
        }

可以看到。此时的线程发生了改变

并且注意到,异步中和异步结束后的线程还是一致,前面提到是随机,难道真这么巧?在多次改变for的次数下,貌似不是随机的,难道除了自定义现在连委托也,但具体情况在我的认知盲区!!!我们知道切换线程是要消耗存在大量程序上下文开销,所以就猜想这是net给优化了吧

# async+await 和同步执行有什么好处,都是要一个线程干活,一条龙干到底不好吗,非得自己干到一半交给另一个线程去干,别人干活不用开销吗?

这个问题的关键就是有些IO事情真的不需要CPU派一个线程去干,去了也干不了。比如读写文件,以往同步方法时,派一个线程去读写点东西的时候,这个线程肯定是等在那的,读写这个事情是硬盘去做的,线程就是个使者,告诉硬盘要做哪些事就行,硬盘干完活就告诉线程,线程拿着结果开心地继续干下面的活。那异步就是线程通知硬盘去读写后自己就回去该干嘛干嘛去,不必等在硬盘家门口等消息。硬盘读写的时候,这个线程是非阻塞的,可以干其他活,提高效率。硬盘读写完,会通知CPU,叫他派一个线程来拿结果,(如果框架是单线程的,那还是这个线程过来)这个线程拿到结果后会继续执行之前那个线程未干完的活,至于上下文信息,底层会交代好,不在这里讨论,太深。这个IO事情有很多,比如访问数据库,请求网络等。
posted @ 2023-01-31 23:50  long-livece  阅读(376)  评论(0编辑  收藏  举报