C#線程學習筆記十:async & await入門三

    1、Task.Yieldhtml

    Task.Yield簡單來講就是建立時就已經完成的Task,或者說執行時間爲0的Task,或者說是空任務,也就是在建立時就將Task的IsCompeted值設置爲0。異步

    咱們知道await的Task完成時會釋放線程,而後從線程池中申請新的線程繼續執行await以後的代碼,那產生的空任務又意義何在呢?async

    事實上,Task.Yield產生的空任務僅僅是借await作嫁衣來達到線程切換的目的,即讓await以後的操做從新去線程池排隊申請新線程來繼續執行。ide

    這樣一來,假若有一個優先級低但執行時間長的任務,能夠將它拆分紅多個小任務,每一個小任務執行完成後就從新去線程池中排隊申請新線程來執行oop

下一個小任務,這樣任務就不會一直霸佔着某個線程了(出讓執行權),讓別的優先急高或執行時間短的任務能夠去執行,而不是乾瞪眼着急。spa

    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

    運行結果以下:線程

    2、在WinForm中使用異步Lambda表達式3d

        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

    運行結果以下:code

    3、滾動條應用orm

        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

    運行結果以下:

    4、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

    http://www.javashuo.com/article/p-kkqhoveb-e.html

    後記:

    關於更詳細的BackgroundWorker知識,可查看此篇博客:

    http://www.javashuo.com/article/p-gcumjlcm-cw.html

相關文章
相關標籤/搜索