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/ 最终输出结果格 ...
随机推荐
- 2019-10-16:渗透测试,基础学习,burpsuit学习,爆破的四种方式学习
Burp Suite 是用于攻击web 应用程序的集成平台,包含了许多工具.Burp Suite为这些工具设计了许多接口,以加快攻击应用程序的过程.所有工具都共享一个请求,并能处理对应的HTTP 消息 ...
- 数据降维-PCA主成分分析
1.什么是PCA? PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法.PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特 ...
- Hbase初识
简介 数据模型 相关数据库 典型应用 优势 劣势 key-value Redis 缓存 快速查询 存储数据缺乏结构化 列族 Cassandra,Hbase 分布式的文件系统,大规模的数据存储 易于分布 ...
- Scrapy持久化存储-爬取数据转义
Scrapy持久化存储 爬虫爬取数据转义问题 使用这种格式,会自动帮我们转义 'insert into wen values(%s,%s)',(item['title'],item['content' ...
- C语言I博客作业01
C语言I博客作业01 作业1 这个作业属于哪个课程? C语言程序设计I 这个作业要求在哪里? https://edu.cnblogs.com/campus/zswxy/CST2019-2/homewo ...
- ios图片适配问题
在不同的系统中显示不同的图片 代码优化 抽取一个分类
- 开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门
点击下载<Knative 云原生应用开发指南> 自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注.Knative 在 Kubernetes 之上提供了一套完整的应 ...
- git配置文件—— .gitattributes
目录 .gitattributes 文档 1. gitattributes文件以行为单位设置一个路径下所有文件的属性,格式如下: 2. 在gitattributes文件的一行中,一个属性(以text属 ...
- 放大镜效果 --- enlarge.js
html页面: 注释:遮罩层的大小取决于 ===>layerwidth/layerheight = largewidth/largeheight enlarge.js页面 /* jqu ...
- go break的使用
文章目录 一.踩得坑 二.break的使用 1.break用于for循环 2. break用于select 3.break用于嵌套循环 4.break label . goto label .cont ...