C#多线程系列之线程完成数
作者:痴者工良 发布时间:2021-12-24 03:07:47
解决一个问题
假如,程序需要向一个 Web 发送 5 次请求,受网路波动影响,有一定几率请求失败。如果失败了,就需要重试。
示例代码如下:
class Program
{
private static int count = 0;
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
new Thread(HttpRequest).Start(); // 创建线程
// 用于不断向另一个线程发送信号
while (count < 5)
{
Thread.Sleep(100);
}
Console.WriteLine("任务执行完毕");
}
// 模拟网络请求
public static void HttpRequest()
{
Console.WriteLine("开始一个任务");
// 随机生成一个数,如果为偶数,则模拟请求失败
bool isSuccess = (new Random().Next(0, 10)) % 2 == 0;
// ... ...模拟请求 HTTP
Thread.Sleep(TimeSpan.FromSeconds(2));
// 请求失败则重试
if (!isSuccess)
{
Console.WriteLine($"请求失败,count={count}");
new Thread(() =>
{
HttpRequest();
}).Start();
return;
}
// 完成一次任务,+1
Interlocked.Add(ref count,1);
Console.WriteLine($"完成任务,count={count}");
}
}
代码太糟糕了,但我们可以使用 CountdownEvent 类来改造它。
CountdownEvent 类
表示在计数变为零时处于有信号状态的同步基元。
也就是说,设定一个计数器,每个线程完成后,就会减去 1 ,当计数器为 0 时,代表所有线程都已经完成了任务。
构造函数和方法
CountdownEvent 类的构造函数如下:
构造函数 | 说明 |
---|---|
CountdownEvent(Int32) | 使用指定计数初始化 CountdownEvent 类的新实例。 |
CountdownEvent 类的常用方法如下:
方法 | 说明 |
---|---|
AddCount() | 将 CountdownEvent 的当前计数加 1。 |
AddCount(Int32) | 将 CountdownEvent 的当前计数增加指定值。 |
Reset() | 将 CurrentCount 重置为 InitialCount 的值。 |
Reset(Int32) | 将 InitialCount 属性重新设置为指定值。 |
Signal() | 向 CountdownEvent 注册信号,同时减小 CurrentCount 的值。 |
Signal(Int32) | 向 CountdownEvent 注册多个信号,同时将 CurrentCount 的值减少指定数量。 |
TryAddCount() | 增加一个 CurrentCount 的尝试。 |
TryAddCount(Int32) | 增加指定值的 CurrentCount 的尝试。 |
Wait() | 阻止当前线程,直到设置了 CountdownEvent 为止。 |
Wait(CancellationToken) | 阻止当前线程,直到设置了 CountdownEvent 为止,同时观察 CancellationToken。 |
Wait(Int32) | 阻止当前线程,直到设置了 CountdownEvent 为止,同时使用 32 位带符号整数测量超时。 |
Wait(Int32, CancellationToken) | 阻止当前线程,直到设置了 CountdownEvent 为止,并使用 32 位带符号整数测量超时,同时观察 CancellationToken。 |
Wait(TimeSpan) | 阻止当前线程,直到设置了 CountdownEvent 为止,同时使用 TimeSpan 测量超时。 |
Wait(TimeSpan, CancellationToken) | 阻止当前线程,直到设置了 CountdownEvent 为止,并使用 TimeSpan 测量超时,同时观察 CancellationToken。 |
API 比较多,没事,我们来慢慢了解它。
示例
我们来编写一个场景代码,一个有五件事,需要完成,分别派出 5 个人去实现。
.Wait();
用在一个线程中,这个线程将等待其它完成都完成任务后,才能继续往下执行。
Signal();
用于工作线程中,向 CountdownEvent 对象发送信号,告知线程已经完成任务,然后 CountdownEvent.CurrentCount
将减去 1。
当计数器为 0 时,阻塞的线程将恢复执行。
代码示例如下:
class Program
{
// 手头上有 5 件事
private static CountdownEvent countd = new CountdownEvent(5);
static void Main(string[] args)
{
Console.WriteLine("开始交待任务");
// 同时叫 5 个人,去做 5 件事
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(DoOne);
thread.Name = $"{i}";
thread.Start();
}
// 等他们都完成事情
countd.Wait();
Console.WriteLine("任务完成,线程退出");
Console.ReadKey();
}
public static void DoOne()
{
int n = new Random().Next(0, 10);
// 模拟要 n 秒才能完成
Thread.Sleep(TimeSpan.FromSeconds(n));
// 完成了,减去一件事
countd.Signal();
Console.WriteLine($" {Thread.CurrentThread.Name}完成一件事了");
}
}
示例很简单,每个线程在完成自己的任务时,需要调用 Signal()
方法,使得计数器减去1。
.Wait();
可以等待所有的任务完成。
需要注意的是,如果不调用 Signal()
或者计数器一直不为0,那么 Wait()
将无限等待。
当然,Wait()
可以设置等待时间,
另外我们也看到了常用方法中有 AddCount()
、Reset()
等。
这个类的等待控制方式比较宽松,Wait()
后,到底什么时候才能执行,全凭其它线程自觉。
如果发现线程执行任务失败,我们可以不调用 Signal()
或者 使用 AddCount()
来增加次数,进行重试
来源:https://www.cnblogs.com/whuanle/p/12733958.html


猜你喜欢
- 一、介绍以及编解码流程MediaCodec 类可用于访问低级媒体编解码器,即编码器/解码器组件。它是 Android 低级多媒体支持基础结构
- 什么是异步调用?异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等
- MyBatis简介MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参
- 一、前言: 我手机从来不装这些东西,不过,有次看到同事的android手机上,有个QQ管家在桌面上浮着,同事拖动管家时,管家就变成一只鸟,桌
- 注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,项目是myb
- 本文实例讲述了C#实现DataSet内数据转化为Excel和Word文件的通用类。分享给大家供大家参考,具体如下:前不久因为项目的需要写的一
- 题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。程序分析:请抓住分子与分母的变化规
- 防止程序运行多个实例的方法有多种,如:通过使用互斥量和进程名等.而我想要实现的是:在程序运行多个实例时激活的是第一个实例,使其获得焦点,并在
- 描述说明:public class TryCatchStu { /*try catch:自己处理异常 *t
- 一、摘要总结基于C#的UDP协议的同步通信。 二、实验平台Visual Studio 2010 三、实验原理UDP传输协
- 概述递归:指在当前方法内调用自己的这种现象。递归的分类:递归分为两种,直接递归和间接递归。直接递归称为方法自身调用自己。间接递归可以A方法调
- 一、事务隔离级别①介绍数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事 务与其他事务隔离的程度称为
- Java语言是简单的:Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用。另一方面,Java丢弃了C++中很少使
- 本文实例为大家分享了Android使用GridView实现横向滚动效果的具体代码,供大家参考,具体内容如下第一次做横向滑动,看了一些列子,基
- 这里介绍通过委托取消Button事件switch-case的方法。需要注意的是,事先要按顺序在各个Button的Tag属性中设置0、1、2、
- 1.创建一个redis maven项目,在pom中添加如下信息spring boot 版本 <parent> <group
- 代理模式代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采
- 本文实例为大家分享了Java实现图形化界面日历的具体代码,供大家参考,具体内容如下此程序主要功能实现了可以根据用户选择的年月日来定位日期,日
- 本文介绍了两种密码加密的方法,这两种很常见可以再百度随意找到。1.摩斯密码;说道密码加密不得不提的方法。很是经典。首先说一下他的对照表,直接
- 一、线程池使用场景•单个任务处理时间短•将需处理的任务数量大二、使用Java线程池好处1、使用new Thread()创建线程的弊端:•每次