C#中的异步多线程补充1

1、都是同步情况下的示例

        static int CPUMission(int val)
        {
            Console.WriteLine($"CPU Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            for (long i = 0; i < 100_000_000; i++) ;
            Console.WriteLine($"CPU End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            return val;
        }
        static int IOMission(int val)
        {
            Console.WriteLine($"IO Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Thread.Sleep(3000);
            Console.WriteLine($"IO End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            return val;
        }
        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            //Task<int> t1 = Task.Run(() => IOMission(3));
            int t1 = IOMission(3);
            int s1 = CPUMission(3);
            Console.WriteLine($"Main Thread End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Console.ReadLine();
        }

输出:同步按顺序依次执行,直观,简单

Main Thread Start:1
IO Start:1
IO End:1
CPU Start:1
CPU End:1
Main Thread End:1

2、启用线程池线程处理IO任务的情况

        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Task<int> t1 = Task.Run(() => IOMission(3));
            //int t1 = IOMission(3);
            int s1 = CPUMission(3);
            Console.WriteLine($"Main Thread End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Console.ReadLine();
        }

输出:

Main Thread Start:1
CPU Start:1
IO Start:4
CPU End:1
Main Thread End:1
IO End:4

可以发现,因为IO任务是通过另开线程执行,所以IO任务和CPU任务之间的顺序就是不可预测的了。

3、现在调用结果

        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Task<int> t1 = Task.Run(() => IOMission(3));
            //int t1 = IOMission(3);
            int s1 = CPUMission(3);
            Console.WriteLine(t1);
            Console.WriteLine(s1);
            Console.WriteLine($"Main Thread End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Console.ReadLine();
        }

输出:

Main Thread Start:1
CPU Start:1
IO Start:4
CPU End:1
System.Threading.Tasks.Task`1[System.Int32]
3
Main Thread End:1
IO End:4

可以看到,虽然调用WriteLine方法打印t1,但t1的返回值是个Task<int>,并不是想要拿到的值,并且此时IO所在线程仍然不是同步的。(IO End在Main Thread End之后出现)

4、调用Task.Result

把Console.WriteLine(t1)修改成Console.WriteLine(t1.Result);

输出结果如下:

Main Thread Start:1
CPU Start:1
IO Start:4
CPU End:1
IO End:4
3
3
Main Thread End:1

可以看到取得了输出结果,并且按照同步地方式执行了后面的语句,也就是说,Task.Result会阻塞当前线程,让其等待Task.Result取得结果,但在之前,异步和同步方法执行顺序仍然是不可预测的。

5、异步方法

通过调用一个异步方法来调用IOMission

        static int IOMission(int val)
        {
            Console.WriteLine($"IO Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Thread.Sleep(3000);
            Console.WriteLine($"IO End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            return val;
        }
        static async Task<int> UseIOMission(int val)
        {
            int result = await Task.Run(() => IOMission(val));
            return result;
        }

调用

        static void Main(string[] args)
        {
            Console.WriteLine($"Main Thread Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Task<int> t1 = UseIOMission(3);
            //Task<int> t1 = Task.Run(() => IOMission(3));
            //int t1 = IOMission(3);
            int s1 = CPUMission(3);
            Console.WriteLine(t1.Result);
            Console.WriteLine(s1);
            Console.WriteLine($"Main Thread End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            Console.ReadLine();
        }

输出结果:

Main Thread Start:1
IO Start:4
CPU Start:1
CPU End:1
IO End:4
3
3
Main Thread End:1

 这样可能还看不到区别,但如果在异步方法中打印一下线程Id

        static async Task<int> UseIOMission(int val)
        {
            Console.WriteLine($"UseIO Start:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            int result = await Task.Run(() => IOMission(val));
            Console.WriteLine($"UseIO End:{Thread.CurrentThread.ManagedThreadId.ToString()}");
            return result;
        }

Main Thread Start:1
UseIO Start:1
IO Start:4
CPU Start:1
CPU End:1
IO End:4
UseIO End:4
3
3
Main Thread End:1

会看到await前后线程Id会改变,也就是说,await Task.Run是异步开启,而Task.Run本身是同步地开启新线程,这一步比较细节,不一定说的正确,会在后面进一步补充修正。

补充一个小例子:

        private void SyncExecuteLongTimeMission()
        {
            Thread.Sleep(3000);
            MessageBox.Show("Mission End");
        }
        private async void AsyncExecuteLongTimeMission()
        {
           await Task.Run(() => SyncExecuteLongTimeMission());
            MessageBox.Show("Async End");
        }

        private void NoAsync_Click(object sender, EventArgs e)
        {
            SyncExecuteLongTimeMission();
        }

        private void Async_Click(object sender, EventArgs e)
        {
            AsyncExecuteLongTimeMission();
        }

        private void OnlyTask_Click(object sender, EventArgs e)
        {
            Task.Run(() => SyncExecuteLongTimeMission());
            MessageBox.Show("Task.Run End");
        }

使用winForm来查看,单纯的Task.Run任务也不会阻塞UI主线程,但会先行显示Task.Run End然后才是Mission End,但如果是在async方法中await Task.Run,那么不仅不会阻塞主线程,还会先Mission End然后才是Async End。

也就是说,async await提供了一种以同步方式调用异步方法的方式,它不仅不会阻塞主线程,还会以同步调用的感觉来返回值,尽管它实际上是异步方法。

posted @ 2020-06-15 17:46  NicolasLiaoRan  阅读(135)  评论(0编辑  收藏  举报