.NET中按预定顺序执行任务
更新记录
本文迁移自Panda666原博客,原发布时间:2021年7月1日。
一、说明
在.NET中线程可以定义按先后顺序进行执行,适合部分有先后次序的业务逻辑。Task也可以按照预定义的先后顺序执行。现在我们分别用Thread和Task实现按次序执行业务逻辑。

二、使用ManualResetEvent类实现Thread的执行顺序
ManualResetEvent类表示 线程同步事件,通过信号机制来控制 多个线程。通过手动控制信号,实现线程通过信号互相通信。通常用于控制线程的先后顺序。注意:除了可以用ManualResetEvent类还可以使用AutoResetEvent类实现线程同步操作。
2.1基本使用
ManualResetEvent构造函数
ManualResetEvent的构造函数如下:
private static ManualResetEvent mre = new ManualResetEvent(false);
构造函数的参数指示信号的初始状态。
true 表示“开启”,线程可以访问资源,WaitOne处时,阻塞恢复正常执行。
false 表示“关闭”,线程碰到WaitOne处时,则发送阻塞暂停。
Set()方法
将事件状态设置为“开启”,类似“绿灯”可以通行。
被WaitOne() 方法阻塞的一/多个线程将可以继续执行代码。
Reset()方法
将事件状态设置为“关闭”,类似“红灯”禁止通行。
线程执行到WaitOne()方法时,就发生阻塞,阻止当前线程继续执行。
实现了和构造函数传入默认值false一样的效果。
直接再次接收信号(调用Set方法),才会再次执行被阻塞的代码。
WaitOne()方法
WaitOne()方法用于等待信号。
当事件状态设置为“开启”时,不进行阻塞,代码继续执行。
当事件状态设置为“关闭”时,进行阻塞,代码不继续执行。
WaitOne()方法发生阻塞的情况:
- 事件同步对象一开始就设置信号为false
- 调用了事件同步对象的Reset()方法
WaitOne()方法发生不阻塞的情况: - 事件同步对象一开始就设置信号为true
- 调用了事件同步对象的Set()方法
2.2实例:主线程控制被控制的线程
using System;
using System.Threading;
namespace ConsoleApplication2
{
/// <summary>
/// 测试类
/// </summary>
class PandaTestClass
{
/// <summary>
/// 测试使用的被控制的线程
/// </summary>
public Thread TestThread { get; set; } = null;
/// <summary>
/// 信号事件对象
/// 参数设置为false,需要手动开启
/// </summary>
public ManualResetEvent manualEvent = new ManualResetEvent(false);
/// <summary>
/// 构造函数
/// </summary>
public PandaTestClass()
{
//线程实例化
TestThread = new Thread(this.Run);
//线程执行
TestThread.Start();
}
/// <summary>
/// 线程具体执行的内容
/// </summary>
private void Run()
{
//模拟耗时的事情
while (true)
{
//根据信号是否进行阻塞当前事情
this.manualEvent.WaitOne();
//打印出当前线程Id
Console.WriteLine("线程id:{0}", Thread.CurrentThread.ManagedThreadId);
//模拟耗时的事情
Thread.Sleep(2000);
}
}
/// <summary>
/// 开启
/// </summary>
public void Start()
{
//发送信号(设置为开启信号)
this.manualEvent.Set();
}
/// <summary>
/// 停止
/// </summary>
public void Stop()
{
//关闭信号(设置有无信号)
this.manualEvent.Reset();
}
}
class Program
{
static void Main(string[] args)
{
//新建测试使用的类型实例
PandaTestClass pandaTestClass = new PandaTestClass();
//无限循环获得用户的输入
while (true)
{
Console.WriteLine("输入 STOP ,后台线程会挂起");
Console.WriteLine("输入 START,后台线程会执行");
//获得用户输入
string userInput = (Console.ReadLine()).ToLower().Trim();
//停止被控制的线程
if (userInput == "stop")
{
Console.WriteLine("线程停止运行");
//停止被控制的线程
pandaTestClass.Stop();
}
//开启被控制的线程
if (userInput == "start")
{
Console.WriteLine("线程开启运行");
//停止被控制的线程
pandaTestClass.Start();
}
}
}
}
}
2.3实例:阻塞主线程来运行被控制线程
using System;
using System.Threading;
namespace PandaTestNamespace
{
class Program
{
//新建线程同步信号事件对象
//参数设置为false表示默认无信号
static ManualResetEvent mnlEvt = new ManualResetEvent(false);
//主线程
static void Main(string[] args)
{
//被控制的线程
Thread th = new Thread(() =>
{
int n = 1;
int result = 0;
while (n <= 100)
{
// 模拟耗时任务
Thread.Sleep(20);
result += n;
Console.WriteLine("正在计算:{0}", n++);
}
Console.WriteLine("计算结果:{0}", result);
//将事件状态设置为开启信号,继续执行WaitOne后续的代码
mnlEvt.Set();
});
//开启线程
th.Start();
Console.WriteLine("正在等待线程计算……");
//因为信号同步事件对象初始化为无信号
//所以这里造成阻塞,需要等待信号到来
//这时候系统会执行被控制的线程
mnlEvt.WaitOne();
Console.WriteLine("计算完毕!");
//测试使用的等待
Console.ReadKey();
}
}
}
2.4实例:按次序依次执行3个线程
using System;
using System.Threading;
namespace PandaTestNamespace
{
class Program
{
//新建线程同步信号事件对象
//参数设置为false表示默认无信号
static ManualResetEvent mnlEvt1 = new ManualResetEvent(false);
static ManualResetEvent mnlEvt2 = new ManualResetEvent(false);
static ManualResetEvent mnlEvt3 = new ManualResetEvent(false);
//主线程
static void Main(string[] args)
{
//被控制的线程1
Thread th1 = new Thread(() =>
{
int n = 1;
int result = 0;
while (n <= 100)
{
// 模拟耗时任务
Thread.Sleep(20);
result += n;
Console.WriteLine("线程1正在计算:{0}", n++);
}
Console.WriteLine("线程1计算结果:{0}", result);
//将事件状态设置为开启信号,继续执行WaitOne后续的代码
mnlEvt1.Set();
});
//开启线程1
th1.Start();
//被控制的线程2
Thread th2 = new Thread(() =>
{
//等待任务1完成
mnlEvt1.WaitOne();
int n = 1;
int result = 0;
while (n <= 100)
{
// 模拟耗时任务
Thread.Sleep(20);
result += n;
Console.WriteLine("线程2正在计算:{0}", n++);
}
Console.WriteLine("线程2计算结果:{0}", result);
//将事件状态设置为开启信号,继续执行WaitOne后续的代码
mnlEvt2.Set();
});
//开启线程2
th2.Start();
//被控制的线程3
Thread th3 = new Thread(() =>
{
//等待任务2完成
mnlEvt2.WaitOne();
int n = 1;
int result = 0;
while (n <= 100)
{
// 模拟耗时任务
Thread.Sleep(20);
result += n;
Console.WriteLine("线程3正在计算:{0}", n++);
}
Console.WriteLine("线程3计算结果:{0}", result);
//将事件状态设置为开启信号,继续执行WaitOne后续的代码
mnlEvt3.Set();
});
//开启线程3
th3.Start();
Console.WriteLine("正在等待线程计算……");
//因为信号同步事件对象初始化为无信号
//所以这里造成阻塞,需要等待信号到来
//这时候系统会执行被控制的线程
mnlEvt3.WaitOne();
Console.WriteLine("计算完毕!");
//测试使用的等待
Console.ReadKey();
}
}
}
三、使用Task任务延续
在Task中可以使用任务延续的概念实现我们上面Thread想要的效果。通过Task.ContinueWith()方法进行任务延续。
3.1串联任务
上一个任务完成后再进行下一个任务。
Task<int> task1 = Task.Run(() => { return 10; })
.ContinueWith((preivewTask) =>{ return preivewTask.Result + 10; })
.ContinueWith((previewTask) => { return previewTask.Result + 10; });
Console.WriteLine(task1.Result); //30
3.2实例:任务延续,任务123会按顺序执行
using System.Threading.Tasks;
//任务1
Task t1 = Task.Run(() => {
Console.WriteLine("T1 Begin Task");
}).ContinueWith(t1=> {
Console.WriteLine("T1 Continue Task");
});
//任务2
Task t2 = t1.ContinueWith(t1 => {
Console.WriteLine("T2 Continue Task");
});
//任务3
Task t3 = t2.ContinueWith(t2 => {
Console.WriteLine("T3 Continue Task");
});
//等待所有任务完成
Task.WaitAll(new Task[] { t1, t2, t3 });
//output
// T1 Begin Task
// T1 Continue Task
// T2 Continue Task
// T3 Continue Task
// 执行完成
.NET中按预定顺序执行任务的更多相关文章
- testNG之顺序执行
@Test testNG1.java: import org.testng.annotations.Test; public class testNG1 { @Test public void t ...
- SQL 中 SELECT 语句的执行顺序
好像自已在书写 SQL 语句时由于不清楚各个关键字的执行顺序, 往往组织的 SQL 语句缺少很好的逻辑, 凭感觉 "拼凑" ( 不好意思, 如果您的 SQL 语句也经常 " ...
- SQLServer2005中查询语句的执行顺序
SQLServer2005中查询语句的执行顺序 --1.from--2.on--3.outer(join)--4.where--5.group by--6.cube|rollup--7.havin ...
- 容易被忽略的事----sql语句中select语句的执行顺序
关于Sql中Select语句的执行顺序,一直很少注意这个问题,对于关键字的使用也很随意,至于效率问题,因为表中的数据量都不是很大,所以也不是很在意. 今天在一次面试的时候自己见到了,感觉没一点的印象, ...
- Qt中连接到同一signal的多个slots的执行顺序问题(4.6以后按连接顺序执行)
起源 前些天忘记在哪儿讨论过这个问题,今天在csdn又看到有网友问这个问题,而其他网友却无一例外的给出了“无序”这个答案. Manual Qt的问题,当manual中有明确文字说明时,我们应该以Qt的 ...
- js在html中的加载执行顺序
1.加载顺序:引入标记<script />的出现顺序,依次加载 页面上的Javascript代码是HTML文档的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记< ...
- <转载>linux gcc编译器中使用gdb单步调试程序,程序不是顺序执行的。
原文地址http://blog.csdn.net/abc78400123/article/details/6779108 在用gdb调试,使用s 或n单步执行程序时,发现程序不是按顺序运行的,有时莫名 ...
- 更优雅的方式: JavaScript 中顺序执行异步函数
火于异步 1995年,当时最流行的浏览器--网景中开始运行 JavaScript (最初称为 LiveScript). 1996年,微软发布了 JScript 兼容 JavaScript.随着网景.微 ...
- Android中让多个线程顺序执行探究
线程调度是指按照特定机制为多个线程分配CPU的使用权. 有两种调度模型:分时调度模型和抢占式调度模型. 分时调度模型:是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片. ...
随机推荐
- PowerBI开发:用自然语言来探索数据--Q&A
Power BI报表的用户,肯定会被Q&A的功能惊艳到,在查看报表时,仅仅通过输入文本就可以探索数据,并且结果是可视化的,更令人惊艳的时,结果几乎是实时显示出来的.这使得Q&A Vis ...
- Springboot之Actuator的渗透测试和漏洞利用
背景概述 Spring的生态很优秀,而使用Spring Boot的开发者也比较多. Actuator是Spring Boot提供的对应用系统的监控和管理的集成功能,可以查看应用配置的详细信息,例如自动 ...
- Codeforces Round #754 (Div. 2), problem: (A) A.M. Deviation泪目 万万没想到狂wa是因为这
Problem - A - Codeforces 题目 题意很简单每次操作可以使得a1 a2 a3任意两个数分别+1 -1 求最后使得a+c-2b绝对值的最小值 BUG就是最后忽略了-2和2这一点 ...
- Java 从零开始实现一个画图板、以及图像处理功能,代码可复现
Java 从零开始实现一个画图板.以及图像处理功能,代码可复现 这是一个学习分享博客,带你从零开始实现一个画图板.图像处理的小项目,为了降低阅读难度,本博客将画图板的一步步迭代优化过程展示给读者,篇幅 ...
- js数组操作集合
1. join() 功能:将数组中所有元素都转化为字符串并连接在一起. 2. reverse() 功能:将数组中的元素颠倒顺序. 3. concat() 功能:数组拼接的功能 ,返回新数组,原数组不受 ...
- Promql基础语法2
数据样本 直方图类型 delta函数 运算操作 数学运算 node_disk_info / 100 当瞬时向量与标量之间进行数学运算时,数学运算符会依次作用域瞬时向量中的每一个样本值,从而得到一组新的 ...
- OA办公软件篇(三)—审批流
背景 作用 迭代历程 具体实现 写在最后 背景 在前面两篇文章中,我们分别讲了组织架构和权限管理,今天我们来讲一个跟组织架构关系比较密切的功能-审批流. 审批流,通俗来说就是一个完整的审批流程,是 ...
- 【CSAPP】Architecture Lab 实验笔记
archlab属于第四章的内容.这章讲了处理器体系结构,就CPU是怎样构成的.看到时候跃跃欲试,以为最后实验是真要去造个CPU,配套资料也是一如既往的豪华,合计四十多页的参考手册,一大包的源码和测试程 ...
- 基于mybatis的java代码生成存储过程
问题: 项目中目前使用mybatis操作数据库,使用插件(mybatis-generator)自动生成代码,对于增改查,使用存储过程实现了一版本,方便使用. insert代码生成器用法: insert ...
- Android 12(S) 图像显示系统 - SurfaceFlinger GPU合成/CLIENT合成方式 - 随笔1
必读: Android 12(S) 图像显示系统 - 开篇 一.前言 SurfaceFlinger中的图层选择GPU合成(CLIENT合成方式)时,会把待合成的图层Layers通过renderengi ...