一、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入门三的更多相关文章

  1. Java学习笔记十二--集合(三)

    第一节课 返回值 方法名 作用 void add(index,elemnet) 在指定的索引处添加元素 object get(index) 返回指定索引处的元素 int indexOf(object) ...

  2. C#线程学习笔记九:async & await入门二

    一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...

  3. C#线程学习笔记八:async & await入门一

    一.涉及内容 async & await是C# 5.0引入的,控制台输出所使用的$符号(拼接字符串)是C# 6.0引入的,其功能类似于string.Format()方法. 二.多线程.异步.同 ...

  4. Oracle RAC学习笔记:基本概念及入门

    Oracle RAC学习笔记:基本概念及入门 2010年04月19日 10:39 来源:书童的博客 作者:书童 编辑:晓熊 [技术开发 技术文章]    oracle 10g real applica ...

  5. Linux内核学习笔记-1.简介和入门

    原创文章,转载请注明:Linux内核学习笔记-1.简介和入门 By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  6. go微服务框架kratos学习笔记十(熔断器)

    目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...

  7. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  8. jQuery学习笔记 - 基础知识扫盲入门篇

    jQuery学习笔记 - 基础知识扫盲入门篇 2013-06-16 18:42 by 全新时代, 11 阅读, 0 评论, 收藏, 编辑 1.为什么要使用jQuery? 提供了强大的功能函数解决浏览器 ...

  9. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

随机推荐

  1. Paramiko的SSH和SFTP使用

    目录 1. 概述 2. Paramiko的基本使用 2.1 SSHClient关键参数介绍 2.2 SSHClient常用示例 2.2.1 通过用户名和密码方式登陆: 2.2.2 通过用户名和密码方式 ...

  2. BeanUtils.copyProperties()怎样去掉字段首尾的空格

    背景 下午三时许,笔者正戴着耳机听着歌开心的敲着bug,忽然听到办公室的吵架声,原来是ios开发和产品小姐姐吵起来了,为了一个车牌号的校验问题.起因是ios传的车牌号没有将字符串的首尾空格去掉,后端直 ...

  3. Java方法的可变参数

    class Demo { public static int sum(int ... data) { //此处可以传递一个数组,也可以是多个参数 int sum = 0; for (int i : d ...

  4. vue下谷歌浏览器的扩展程序(vue-devtools-master)

    1,在百度网盘中下载压缩包,网盘地址:https://pan.baidu.com/s/1BnwWHANHNyJzG3Krpy7S8A ,密码:xm6s 2,将压缩包解压到F盘,F:\chromeVue ...

  5. 构建调试Linux内核网络代码的环境MenuOS系统

    构建MenuOS系统 1.将指定文件拷贝到本地: git clone https://github.com/mengning/linuxnet.git 此过程可能需要输入github账号和密码. 2. ...

  6. Nginx+MySQL+PHP+Redis多机部署(测试发布discuz论坛)

    链接:LNMP+Redis单机部署 1.实战多机部署环境 nginx服务器: 192.168.1.3 php服务器:    192.168.1.4 mysql服务器: 192.168.1.10 red ...

  7. Java继承、构造、重写

    Music mu=new Music(); Musc m=mu;//地址一样   继承:Java只支持单继承,不支持多继承. Java支持多层(重)继承(继承体系). 如果类之间存在着:is a 的关 ...

  8. 【Python成长之路】从零学GUI -- 制作智能聊天机器人

    [写在前面] 鹏哥:最近老惹小燕同学不开心,结果都没人陪我聊天了.哎,好无聊呀! 肥宅男:女朋友什么的最无聊了,还没我的图灵机器人好玩. 鹏哥:图灵?好巧,和我部门同名. [效果如下] [实现过程] ...

  9. Pycharm常见快捷键

    Ctrl+/注释(取消注释)选择的行 Shift + Enter开始新行 Ctrl + Enter智能换行 TAB Shift+TAB缩进/取消缩进所选择的行 Ctrl + Alt + I自动缩进行 ...

  10. segment树(线段树)

    线段树(segment tree)是一种Binary Search Tree或者叫做ordered binary tree.对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+ ...