• 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
            }
    
            /// <summary>
            /// 循环
            /// </summary>
            /// <param name="num"></param>
            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);
            }
    
            /// <summary>
            /// 分批出让执行权
            /// </summary>
            /// <param name="times"></param>
            /// <returns></returns>
            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

        运行结果如下:

        二、在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

        运行结果如下:

        三、滚动条应用

            private CancellationTokenSource source;
            private CancellationToken token;
    
            public ProcessBar()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 初始化
            /// </summary>
            private void InitTool()
            {
                progressBar1.Value = 0;
                btnDoIt.Enabled = true;
                btnCancel.Enabled = true;
            }
    
            /// <summary>
            /// 开始任务
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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();
            }
    
            /// <summary>
            /// 取消任务
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnCancel_Click(object sender, EventArgs e)
            {
                if (btnDoIt.Enabled) return;
    
                btnCancel.Enabled = false;
                source.Cancel();
            }
        }
    View Code

        运行结果如下:

        四、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;
            }
    
            /// <summary>
            /// 开始执行后台操作触发,即调用BackgroundWorker.RunWorkerAsync时发生。
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            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毫秒
                }
            }
    
            /// <summary>
            /// 调用BackgroundWorker.ReportProgress(System.Int32)时发生
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                progressBar1.Value = e.ProgressPercentage;  //异步任务的进度百分比
            }
    
            /// <summary>
            /// 当后台操作已完成或被取消或引发异常时发生
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                MessageBox.Show(e.Cancelled ? $@"任务已被取消,已执行进度为:{progressBar1.Value}%" : $@"任务执行完成,已执行进度为:{progressBar1.Value}%");
                progressBar1.Value = 0;
            }
    
            /// <summary>
            /// 开始任务
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnDoIt_Click(object sender, EventArgs e)
            {
                //判断BackgroundWorker是否正在执行异步操作
                if (!bgWorker.IsBusy)
                {
                    bgWorker.RunWorkerAsync();  //开始执行后台操作
                }
            }
    
            /// <summary>
            /// 取消任务
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnCancel_Click(object sender, EventArgs e)
            {
                bgWorker.CancelAsync(); //请求取消挂起的后台操作
            }
    View Code

        运行结果如下:

        参考自:

        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

  • 相关阅读:
    C#获取HTML文件指定DIV内容
    剔除editor编辑器中的HTML标签
    ASP.NET MVC 在WebService中Token的使用方法
    MVC如何在单独的类库中添加区域
    原来写插件还可以选MEF
    EF CodeFirst学习
    .Net EF 学习之model first
    sql分页存储过程,带求和、排序
    软实力
    微软的XML可视化编辑器:XML Notepad 2007
  • 原文地址:https://www.cnblogs.com/atomy/p/12052082.html
Copyright © 2020-2023  润新知