zoukankan      html  css  js  c++  java
  • 异步、多线程、Await/Async、Task

    异步多线程经常被拿来说事,网上文章也是多如牛毛,我也是停留在很菜的水平,痛下决心好好“恶补”一下这块知识。
    还是先放两个官方文档压压惊:使用 Async 和 Await 的异步编程    .NET 中的并行处理、并发和异步编程

    学习前:

    耗时操作知道搞个Task.Run,不用等待结果就后台执行,Api直接返回,多个任务就循环启用Task。控制器方法加上async,调用加上await,别人这么用也跟着,不造为毛。Thread最常用的是Sleep。。。就这样。

    学习后:

    多线程的前世今生就不具体介绍了,简单粗暴的实操文章奉上:Thread、ThreadPool、Task、Parallel的基本用法、区别以及弊端
    通过几个小例子Get不明白的知识点,搞一个winform,启动的同时再带一个控制台,方便输出对比,Program.cs需要加几行代码。大概放了这几个按钮,写了几个输出和调用异步的方法(完整代码在最后)。

    1、同步

    这就不说了,睡3秒顺序执行,有一点注意是文本框信息是没有即时更新的,winform正常情况下是主线程更新控件状态的,因为主线程被阻塞了,所以无法刷新UI,主线程释放后才执行。

    2、异步中更新UI

    Over的时候可以发现更新信息是由线程id为5的线程完成的,跨线程操作winform控件的方法交给委托,见红框。

    3、异步结果更新UI

    如果异步线程需要等待返回值可以这样写更简洁,方法上加上了async关键字,await等待线程执行,那么问题来了,异步不就又变成同步了?意义何在呢?这也是实操前一直不明白的点,这个例子看不出效果,接着操作。

    4、调用异步

    调用异步方法,自己本身也应该是异步的,如果没有async关键字,就会报出警告,并且没有async修饰的方法不能使用await关键字,这个例子只是把异步方法MethodAsync拿出去了,为了演示下面的例子使用。

    5、异步调用死锁

    这里调用异步方法MethodAsyncDeadlock,与MethodAsync的区别就是,把耗时函数的结果返回了,由于主线程没有等待异步调用返回,又输出了异步方法的结果,自然是拿不到返回值,因为异步方法还没有执行完成,所以这线程就“干架”了,卡这了,死锁。

    6、解决死锁

    死锁大部分是代码不规范造成的,代码写懵逼也是常有的事,解决也很简单,await出马,等异步方法完成返回再继续执行就OK了。

    7、Thread、Parallel、Task

    启动方式直接看代码,有一个小坑需要注意,就是多线程访问公共变量问题。

    8、重点来了!!!

    异步编程的意义何在这个哲学问题,换一个控制台例子更能说明问题。很简单,一个控制台、一个类。从执行结果可以看出来,Main方法开始,到MethodAsync异步方法执行,开启Task耗时函数之前都是线程1在干活儿,耗时函数由另外一个线程4执行,开始之前线程1已已经返回,并且输出了Main End,这意味着剩下的不管是耗时函数,还是等待耗时函数完成之后的逻辑,都不在需要主线程等待完成,由别的线程搞定。winform解释不来是因为异步完成之后还是交给了主线程UI去继续执行了,所以线程id总是前后一致。

    9、结论!!!

    实操分析完之后,就明白了之前忘记在哪看到的一句话了:异步能增加网站的吞吐量,但并不会让网站变得更快。
    1、提高网站的响应能力,也是async/await也常见于Web项目的原因。
    2、可以用同步的写法,完成异步操作,需要的时候启动线程并行执行,提高运算或逻辑处理能力,又能等待控制顺序,可以说贼爽。
    参考了:C# 彻底搞懂async/await,也是言简意赅的文章。

    附上代码(粗陋勿喷)

    public partial class AsyncDome : Form
    {
        public AsyncDome()
        {
            InitializeComponent();
        }
    
        //同步
        private void button1_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            Thread.Sleep(3000);
            btnClickAop(sender, "End");
        }
        //异步中更新UI
        private void button2_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            Task.Run(() =>
            {
                Thread.Sleep(3000);
                showConsoleDelegate(bt(sender) + " Over");
            });
            btnClickAop(sender, "End");
        }
        //异步结果更新UI
        private async void button3_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            var t = Task.Run(() =>
            {
                Thread.Sleep(3000);
                return bt(sender) + " Over";
            });
            showConsole(await t);
            btnClickAop(sender, "End");
        }
        //调用异步
        private void button4_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            MethodAsync();
            btnClickAop(sender, "End");
        }
        //异步调用死锁
        private void button5_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            var ResultTask = MethodAsyncDeadlock();
            showConsole(ResultTask.Result); //死锁原因:此行代码与调用方法抢占主线程,导致死锁。
            btnClickAop(sender, "End");
        }
        //解决死锁
        private async void button6_Click(object sender, EventArgs e)
        {
            btnClickAop(sender, "Start", true);
            var ResultTask = MethodAsyncDeadlock();
            showConsole(await ResultTask); //死锁解决:等待调用方法使用完成主线程即可。
            btnClickAop(sender, "End");
        }
        //Thread循环
        private void button7_Click(object sender, EventArgs e)
        {
            //Thread
            for (int i = 0; i < 100; i++)
            {
                int newIndex = i;
                new Thread(new ThreadStart(() => showConsoleDelegate("for: " + newIndex))).Start();
            }
        }
        //Parallel循环
        private void button8_Click(object sender, EventArgs e)
        {
            //Parallel
            Parallel.ForEach<int>(new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, (str) =>
            {
                showConsoleDelegate(str.ToString());
            });
        }
        //Task循环
        private void button9_Click(object sender, EventArgs e)
        {
            //Task
            for (int i = 0; i < 100; i++)
            {
                int newIndex = i;
                Task.Run(() => { showConsoleDelegate("for: " + newIndex); });
            }
        }
    
        #region 输出处理
        private void btnClickAop(object sender, string flag, bool empty = false)
        {
            if (empty)
            {
                txtConsloe.Text = "";
                Console.Clear();
            }
            showConsole(bt(sender) + " " + flag);
        }
    
        private void showConsole(string info)
        {
            info = getInfo(info);
            txtConsloe.Text += info;
            Console.WriteLine(info);
        }
    
        private void showConsoleDelegate(string info)
        {
            info = getInfo(info);
            Console.WriteLine(info);
            txtConsloe.Invoke(new Action(() => { txtConsloe.Text += info; }));
        }
        //输出信息
        private string getInfo(string info)
        {
            return info + " Thread :" + Thread.CurrentThread.ManagedThreadId + " " + DateTime.Now.ToString("G") + Environment.NewLine;
        }
        //获取按钮文本
        private string bt(object sender)
        {
            return ((Button)sender).Text;
        }
        #endregion
    
        #region 异步方法
        private async Task MethodAsync()
        {
            showConsole("MethodAsync Start");
            string Result = await LongTimeMethod();
            showConsole(Result);
            showConsole("MethodAsync End");
        }
        private async Task<string> MethodAsyncDeadlock()
        {
            showConsole("MethodAsyncDeadlock Start");
            string Result = await LongTimeMethod();
            showConsole("MethodAsyncDeadlock End");
            return Result;
        }
    
        private Task<string> LongTimeMethod()
        {
            var task = Task.Run(() =>
            {
                showConsoleDelegate("LongTimeMethod Start ");
                Thread.Sleep(3000);
                showConsoleDelegate("LongTimeMethod End ");
                return "LongTimeMethod Over";
            });
            return task;
        }
        #endregion
    }
    
    测试签名
  • 相关阅读:
    Linux系统安全及应用
    Linux 10 进程和计划的管理任务
    Centos netdata 的安装及配置
    Centos nmon安装及使用
    Python 各种数据类型的了解(上)
    神奇的循环知识
    Pathon的基础知识
    初次遇见Python
    操作系统与编程语言的简介
    计算机硬件基础
  • 原文地址:https://www.cnblogs.com/WinterSir/p/14887025.html
Copyright © 2011-2022 走看看