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; } }
运行结果如下:
二、在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; }
运行结果如下:
三、滚动条应用
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(); } }
运行结果如下:
四、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(); //请求取消挂起的后台操作 }
运行结果如下:
参考自:
后记:
关于更详细的backgroundworker知识,可查看此篇博客:
上一篇: 苹果iPhone 13前瞻:机身更厚、摄像头更凸起
下一篇: 充满诱惑的小卡片