一、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. Towards Universal Object Detection by Domain Attention

    论文及代码 论文地址:https://arxiv.org/abs/1904.04402 代码:http://www.svcl.ucsd.edu/projects/universal-detection ...

  2. 2019-9-27:渗透测试,metasploit-framework初接触

    初次利用metasploit漏洞利用框架,入侵获取win7获取权限 目标机系统:windows7,无修复补丁 目标ip地址:192.168.20.131 任务:利用ms17-010,永恒之蓝漏洞,获取 ...

  3. vue 做一个简单的TodoList

    目录结构 index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"&g ...

  4. fastjson自定义序列化竟然有这么多姿势?

    本文介绍下fastjson自定义序列化的各种操作. 一.什么是fastjson? fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSO ...

  5. Java并发编程:Callable、Future和FutureTask【转】

    原文链接:http://www.cnblogs.com/dolphin0520/p/3949310.html 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这 ...

  6. git本地项目连接私人远程仓库以及遇到的问题

    一.引言 1.最开始的时候,我本地项目连接的是github远程仓库,现在要转到公司的私人远程仓库. 2.我和大家说两个事: (1)本地项目连接github远程仓库, (2)本地项目连接私人远程仓库, ...

  7. CentOS 7 安装 bind 服务 实现内网DNS

    目录 安装 配置 服务管理 测试 安装 废话不多说,直接安装 yum install -y bind bind-utils 配置 [root@jenkins named]# rpm -ql bind ...

  8. php 7.4 的 Unpacking inside arrays (...)

    总的说明 php 7.4 增加了一个很有意思的功能 这是官方说明: Unpacking inside arrays <?php$parts = ['apple', 'pear'];$fruits ...

  9. 基于Win服务的标签打印(模板套打)

    最近做了几个项目,都有在产品贴标的需求 基本就是有个证卡类打印机,然后把产品的信息打印在标签上. 然后通过机器人把标签贴到产品上面 标签信息包括文本,二维码,条形码之类的,要根据对应的数据生成二维码, ...

  10. 【经验分享】linux交叉编译 - openssl动态库

    一.准备工作 1.到openssl官网下载最新版本openssl(如openssl-1.1.1d.tar.gz),上传到linux编译机上(如上传到目录/home/test下),并使用tar -xvf ...