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/ 最终输出结果格 ...
随机推荐
- git 删除误上传的.idea文件
问题: 提交项目的时候忘记添加.gitignore文件,误上传了文件(如.idea)如何解决?(本文以.idea文件夹举例) 1.将项目文件拉取下来 git pull origin master 2. ...
- Github上传大文件(超过100M)
上传大文件(超过100M)到Github 笔者上传操作100M的文件到Github,结果在push的时候会自动终止.然后提示无法上传大文件,就算删除再提交也是报错. 于是,本人写这篇博客就是为了解决这 ...
- java中大整型BigInteger及setBit和testBit方法
最近在修改公司之前的项目,在项目中遇到了权限校验的问题,代码中出现了BigInteger的setBit()testBit()方法,之前未接触过,所以了解了下BigInteger. 在Java中,由CP ...
- day 23 面向对象中类的成员 和嵌套
1.类的成员? 变量.方法.属性 class Foo: # 方法 def __init__(self,name): # 实例变量/字段 self.name = name # 方法 def func(s ...
- Webstrom怎么修改主题
开发Node.js程序,当选首选的是webstorm IDE工具,这个不用解释.但是可能很多习惯其它IDE的同学在使用Webstorm的时候,后感觉webstorm的主题,并不怎么适合自己的审美.就 ...
- 聚类-DBSCAN基于密度的空间聚类
1.DBSCAN介绍 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度 ...
- sql server建库建表(数据库和数据表的常用操作)
数据库和数据表 (开发常用 操作) 一,数据库的创建 一个SQLServer 是由两个文件组成的:数据文件(mdf) 和日志文件(ldf),所以我们创建数据库就是要为其指定数据库名.数据文件和日志文件 ...
- sql server 增删改(查太多了)
表: 学生(*学号,姓名,性别,年龄,专业) create table student( sno ) primary key, sname ) not null, ssex ), sage small ...
- kubeadm join 超时报错 error execution phase kubelet-start: error uploading crisocket: timed out waiting for the condition
解决: swapoff -a kubeadm reset systemctl daemon-reload systemctl restart kubelet iptables -F && ...
- 常用torch代码片段合集
PyTorch常用代码段整理合集 本文代码基于 PyTorch 1.0 版本,需要用到以下包 import collections import os import shutil import tqd ...