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 = ;
var task = YieldPerTimes(num); for (int i = ; i < ; i++)
{
Task.Factory.StartNew(n => Loop((int)n), num / );
} Console.WriteLine($"Sum: {task.Result}");
Console.Read();
#endregion
} /// <summary>
/// 循环
/// </summary>
/// <param name="num"></param>
private static void Loop(int num)
{
for (var i = ; i < num; i++) ;
Console.WriteLine($"Loop->Current thread id is:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep();
} /// <summary>
/// 分批出让执行权
/// </summary>
/// <param name="times"></param>
/// <returns></returns>
private static async Task<int> YieldPerTimes(int num)
{
var sum = ;
for (int i = ; i <= num; i++)
{
sum += i;
if (i % == )
{
Console.WriteLine($"Yield->Current thread id is:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep();
await Task.Yield();
}
}
return sum;
}
}
运行结果如下:

二、在WinForm中使用异步Lambda表达式
public Main()
{
InitializeComponent(); //异步表达式:async (sender, e)
btnDoIt.Click += async (sender, e) =>
{
DoIt(false, "开始搬砖啦...");
await Task.Delay();
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 = ;
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 = ; //完成百分比
const int loopTimes = ; //循环次数
const int increment = / loopTimes; //进度条每次增加的进度值 for (var i = ; i <= loopTimes; i++)
{
if (token.IsCancellationRequested)
{
break;
} try
{
await Task.Delay(, 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 = ;
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 = ; i <= ; i++)
{
//判断程序是否已请求取消后台操作
if (worker.CancellationPending)
{
e.Cancel = true;
break;
} worker.ReportProgress(i * ); //触发BackgroundWorker.ProgressChanged事件
Thread.Sleep(); //线程挂起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 = ;
} /// <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(); //请求取消挂起的后台操作
}
运行结果如下:

参考自:
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#线程学习笔记十:async & await入门三的更多相关文章
- Java学习笔记十二--集合(三)
第一节课 返回值 方法名 作用 void add(index,elemnet) 在指定的索引处添加元素 object get(index) 返回指定索引处的元素 int indexOf(object) ...
- C#线程学习笔记九:async & await入门二
一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...
- C#线程学习笔记八:async & await入门一
一.涉及内容 async & await是C# 5.0引入的,控制台输出所使用的$符号(拼接字符串)是C# 6.0引入的,其功能类似于string.Format()方法. 二.多线程.异步.同 ...
- Oracle RAC学习笔记:基本概念及入门
Oracle RAC学习笔记:基本概念及入门 2010年04月19日 10:39 来源:书童的博客 作者:书童 编辑:晓熊 [技术开发 技术文章] oracle 10g real applica ...
- Linux内核学习笔记-1.简介和入门
原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- go微服务框架kratos学习笔记十(熔断器)
目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- jQuery学习笔记 - 基础知识扫盲入门篇
jQuery学习笔记 - 基础知识扫盲入门篇 2013-06-16 18:42 by 全新时代, 11 阅读, 0 评论, 收藏, 编辑 1.为什么要使用jQuery? 提供了强大的功能函数解决浏览器 ...
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...
随机推荐
- 容器镜像服务联手 IDE 插件,实现一键部署、持续集成与交付
容器技术提供了一种标准化的交付方式,将应用的代码以及代码环境依赖都打包在一起,成为一个与环境无关的交付物,可以被用在软件生命周期的任何阶段,彻底改变了传统的软件交付方式. 甚至可以说,是在容器技术之后 ...
- Base系列编码浅析【base16 base32 base64 base85 base36 base 58 base91 base 92 base62】
Base系列编码浅析 [base16 base32 base64 base85 base36 base 58 base91 base 92 base62] base编码 ...
- Python爬虫之Selenium的常用方法
1.单个元素的选取 find_element_by_id 通过标签属性Id查找元素 find_element_by_name 通过标签属性name查找元素 find_element ...
- JS进阶面试题整理(仅仅整理我做错的题)
前几天看到掘金博客一篇文章,找到了这个JavaScript进阶问题列表:现在把地址贴出来,想找工作或者想要巩固自己JS的同学可以参考 该文档会不定时更新 一.箭头函数 箭头函数相当于匿名函数,并 ...
- C语言I博客作业11
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-1/homework/10132 我在这个课程的 ...
- JavaScript-----3.变量
1.变量的使用 变量在使用的时候分两步:1. 声明变量 2. 赋值 1.1声明变量 //声明变量 var age;//声明一个名字为age的变量 var是JS的一个关键字,用于声明变量,使用该关键字声 ...
- 【Android - 控件】之MD - Snackbar的使用
Snackbar 是 Android 5.0 新特性——Material Design 中的一个控件,用来代替 Toast ,Snackbar与Toast的主要区别是:Snackbar可以滑动退出,也 ...
- PHP的array_walk和array_map函数实现数组值UTF-8转GBK编码
在PHP中,array_walk() 和 array_map()两个函数都可以实现对数组中每个值的修改,比如本例就是将数组中所有的值,由UTF-8编码转成GBK编码. 当然,除了这两个函数,也可以用 ...
- element中 input赋值后无法再次输入值
项目中有个需求,在表格里点击某条数据弹出窗口进行修改值,当时弹出的是input上进行修改,所以当我点击数据的时候,先进行回显原先的数据,再进行修改. 点击某条数据,弹出窗口,进行后台请求,将后台返回的 ...
- 微信小程序——e.target与e.currentTarget的区别
在小程序的点击事件中,我们经常使用这两个属性来传参,看起来效果一样,查了官方文档如下: target:事件源组件对象 currentTarget:当前组件对象 什么意思?我刚开始就有点不懂,那就直接上 ...