C#线程学习笔记十:async &; await入门三

    一、Task.Yield

    Task.Yield简单来说就是创建时就已经完成的Task,或者说执行时间为0的Task,或者说是空任务,也就是在创建时就将Task的IsCompeted值设置为0。

    我们知道await的Task完成时会释放线程,然后从线程池中申请新的线程继续执行await之后的代码,那产生的空任务又意义何在呢?

    事实上,Task.Yield产生的空任务仅仅是借await做嫁衣来达到线程切换的目的,即让await之后的操作重新去线程池排队申请新线程来继续执行。

    这样一来,假如有一个优先级低但执行时间长的任务,可以将它拆分成多个小任务,每个小任务执行完成后就重新去线程池中排队申请新线程来执行

下一个小任务,这样任务就不会一直霸占着某个线程了(出让执行权),让别的优先急高或执行时间短的任务可以去执行,而不是干瞪眼着急。

    class Program
    {
        static void Main(string[] args)
        {
            #region async & await入门三之Task.Yield 
            const int num = 10000;
            var task = YieldPerTimes(num);

            for (int i = 0; i < 10; i++)
            {
                Task.Factory.StartNew(n => Loop((int)n), num / 10);
            }

            Console.WriteLine($"Sum: {task.Result}");
            Console.Read();
            #endregion
        }

        /// 
        /// 循环
        /// 
        /// 
        private static void Loop(int num)
        {
            for (var i = 0; i < num; i++) ;
            Console.WriteLine($"Loop->Current thread id is:{Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(10);
        }

        /// 
        /// 分批出让执行权
        /// 
        /// 
        /// 
        private static async Task<int> YieldPerTimes(int num)
        {
            var sum = 0;
            for (int i = 1; i <= num; i++)
            {
                sum += i;
                if (i % 1000 == 0)
                {
                    Console.WriteLine($"Yield->Current thread id is:{Thread.CurrentThread.ManagedThreadId}");
                    Thread.Sleep(10);
                    await Task.Yield();
                }
            }
            return sum;
        }
    }
View Code

    运行结果如下:

C#线程学习笔记十:async &; await入门三_第1张图片

    二、在WinForm中使用异步Lambda表达式

        public Main()
        {
            InitializeComponent();

            //异步表达式:async (sender, e)
            btnDoIt.Click += async (sender, e) =>
            {
                DoIt(false, "开始搬砖啦...");
                await Task.Delay(3000);
                DoIt(true, "终于搬完了。");
            };
        }

        private void DoIt(bool isEnable, string text)
        {
            btnDoIt.Enabled = isEnable;
            lblText.Text = text;
        }
View Code

    运行结果如下:

C#线程学习笔记十:async &; await入门三_第2张图片

    三、滚动条应用

        private CancellationTokenSource source;
        private CancellationToken token;

        public ProcessBar()
        {
            InitializeComponent();
        }

        /// 
        /// 初始化
        /// 
        private void InitTool()
        {
            progressBar1.Value = 0;
            btnDoIt.Enabled = true;
            btnCancel.Enabled = true;
        }

        /// 
        /// 开始任务
        /// 
        /// 
        /// 
        private async void btnDoIt_Click(object sender, EventArgs e)
        {
            btnDoIt.Enabled = false;

            source = new CancellationTokenSource();
            token = source.Token;

            var completedPercent = 0;               //完成百分比
            const int loopTimes = 10;               //循环次数
            const int increment = 100 / loopTimes;  //进度条每次增加的进度值

            for (var i = 1; i <= loopTimes; i++)
            {
                if (token.IsCancellationRequested)
                {
                    break;
                }

                try
                {
                    await Task.Delay(200, token);
                    completedPercent = i * increment;
                }
                catch (Exception)
                {
                    completedPercent = i * increment;
                }
                finally
                {
                    progressBar1.Value = completedPercent;
                }
            }

            var msg = token.IsCancellationRequested ? $"任务被取消,已执行进度为:{completedPercent}%。" : $"任务执行完成。";
            MessageBox.Show(msg, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

            progressBar1.Value = 0;
            InitTool();
        }

        /// 
        /// 取消任务
        /// 
        /// 
        /// 
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (btnDoIt.Enabled) return;

            btnCancel.Enabled = false;
            source.Cancel();
        }
    }
View Code

    运行结果如下:

C#线程学习笔记十:async &; await入门三_第3张图片

    四、BackgroundWorker

    与async & await不同的是,有时候可能需要一个额外的线程,它在后台持续完成某个任务并不时与主线程通信,这时就需要用到BackgroundWorker类。

(主要用于GUI程序)

        private readonly BackgroundWorker bgWorker = new 
        BackgroundWorker();

        public ProcessBar()
        {
            InitializeComponent();

            //设置BackgroundWorker属性
            bgWorker.WorkerReportsProgress = true;      //能否报告进度更新
            bgWorker.WorkerSupportsCancellation = true; //是否支持异步取消

            //连接BackgroundWorker对象的处理程序
            bgWorker.DoWork += bgWorker_DoWork;
            bgWorker.ProgressChanged += bgWorker_ProgressChanged;
            bgWorker.RunWorkerCompleted += bgWorker_RunWorkerCompleted;
        }

        /// 
        /// 开始执行后台操作触发,即调用BackgroundWorker.RunWorkerAsync时发生。
        /// 
        /// 
        /// 
        private static void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (!(sender is BackgroundWorker worker))
            {
                return;
            }

            for (var i = 1; i <= 10; i++)
            {
                //判断程序是否已请求取消后台操作
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }

                worker.ReportProgress(i * 10);  //触发BackgroundWorker.ProgressChanged事件
                Thread.Sleep(200);              //线程挂起200毫秒
            }
        }

        /// 
        /// 调用BackgroundWorker.ReportProgress(System.Int32)时发生
        /// 
        /// 
        /// 
        private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;  //异步任务的进度百分比
        }

        /// 
        /// 当后台操作已完成或被取消或引发异常时发生
        /// 
        /// 
        /// 
        private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            MessageBox.Show(e.Cancelled ? $@"任务已被取消,已执行进度为:{progressBar1.Value}%" : $@"任务执行完成,已执行进度为:{progressBar1.Value}%");
            progressBar1.Value = 0;
        }

        /// 
        /// 开始任务
        /// 
        /// 
        /// 
        private void btnDoIt_Click(object sender, EventArgs e)
        {
            //判断BackgroundWorker是否正在执行异步操作
            if (!bgWorker.IsBusy)
            {
                bgWorker.RunWorkerAsync();  //开始执行后台操作
            }
        }

        /// 
        /// 取消任务
        /// 
        /// 
        /// 
        private void btnCancel_Click(object sender, EventArgs e)
        {
            bgWorker.CancelAsync(); //请求取消挂起的后台操作
        }
View Code

    运行结果如下:

C#线程学习笔记十:async &; await入门三_第4张图片

    参考自:

    https://www.cnblogs.com/dudu/archive/2018/10/24/task-yield.html

    https://www.cnblogs.com/liqingwen/p/5877042.html

    后记:

    关于更详细的BackgroundWorker知识,可查看此篇博客:

    https://www.cnblogs.com/sparkdev/p/5906272.html

你可能感兴趣的:(技术)