08-C#

c#入门——08练习讲解 根据上一篇学到的知识点,我们来看一下以下练习吧! 1,编写一个程序,对输入的4个整数,求出其中的最大值和最小值,并显示出来。 下面这个方法会比较麻烦,之后我们学习了数组之后会有简单方法 Console.WriteLine("请输入4个整数"); int num1 = Convert.ToInt32(Console.ReadLine()); int num2 = Convert.ToInt32(C... 阅读详情

C#.Net-Thread-学习笔记


一、多线程基础概念

多线程的三大特点:

  • 异步执行:不阻塞主线程,多件事可以同时进行
  • 效率高:充分利用 CPU 等计算机资源
  • 无序性:多个线程的执行顺序不可预测,无法控制

⚠️ 正因为无序性,多线程调试困难,通常只能通过写日志、输出结果、结合线程 ID 来分析问题。

// 获取当前线程 ID,用于区分不同线程
int id = Thread.CurrentThread.ManagedThreadId;

二、Thread 类

基本概念

  • 来源:System.Threading 命名空间
  • 出现时间:.NET Framework 1.0
  • 类型:密封类(sealed class),不可被继承
  • 作用:操作计算机线程资源的帮助类库

开启线程

方式一:ThreadStart(无参数)

ThreadStart threadStart = new ThreadStart(() =>
{
    DoSomething("Richard");
});
Thread thread = new Thread(threadStart);
thread.Start();

方式二:ParameterizedThreadStart(带参数)

ParameterizedThreadStart pts = new ParameterizedThreadStart(obj =>
{
    Console.WriteLine($"参数: {obj}");
});
Thread thread = new Thread(pts);
thread.Start("传递的参数");

常用 API

线程控制
thread.Suspend();       // 暂停线程(不会立即停止)
thread.Resume();        // 恢复执行
thread.Abort();         // 停止线程(向线程抛出异常)
Thread.ResetAbort();    // 取消停止,让线程继续执行

❌ Suspend、Resume、Abort 均已标记为过时(Obsolete),实际开发中不应使用。线程无法真正从外部被强制终止,这些方法只是通过异常机制间接干预,存在安全隐患。

线程等待

方式一:观望式轮询(不推荐)

while (thread.ThreadState != ThreadState.Stopped)
{
    Thread.Sleep(500); // 每 500ms 检查一次
}

❌ 持续轮询会浪费 CPU 时间片,不推荐。

方式二:Join 等待(推荐)

thread.Join();                              // 无限等待,直到线程结束
thread.Join(500);                           // 最多等待 500ms,过时不候
thread.Join(TimeSpan.FromMilliseconds(500)); // 同上,TimeSpan 写法

✅ Join() 是最简洁直接的线程等待方式。

前台线程 vs 后台线程
thread.IsBackground = true;  // 后台线程:程序关闭时,线程立即终止
thread.IsBackground = false; // 前台线程:程序关闭时,等待线程执行完毕再退出
  • 默认:通过 new Thread() 创建的线程是前台线程
  • ThreadPool 中的线程全部是后台线程
  • 主线程(Main 方法所在线程)是前台线程

⚠️ 如果忘记将长时间运行的线程设为后台线程,可能导致程序关闭后进程仍然存活。

线程优先级
thread.Priority = ThreadPriority.Highest; // 最高优先级
thread.Priority = ThreadPriority.Lowest;  // 最低优先级

⚠️ 设置优先级只是提高被优先调度的概率,操作系统不保证严格按优先级执行,不能依赖它来控制执行顺序。


三、Thread 扩展封装

问题背景

多线程天然无序,但有时需要:

  1. 两个操作都在新线程中执行
  2. 同时保证它们的执行顺序

方案一:顺序执行两个委托

把两个委托放进同一个线程,天然保证顺序:

private void CallBackThread(ThreadStart threadStart, Action action)
{
    Thread thread = new Thread(() =>
    {
        threadStart.Invoke(); // 先执行
        action.Invoke();      // 后执行
    });
    thread.Start();
}

方案二:带返回值的异步执行

核心思路:立即开启新线程执行,返回一个"取结果的委托",调用方在需要结果时才触发等待。

private Func<T> CallBackFunc<T>(Func<T> func)
{
    T result = default;

    Thread thread = new Thread(() =>
    {
        result = func.Invoke(); // 新线程中执行
    });
    thread.Start(); // 立即开启,不阻塞

    // 返回一个延迟取值的委托
    return new Func<T>(() =>
    {
        thread.Join(); // 调用时才等待线程完成
        return result;
    });
}

使用示例:

Func<int> asyncFunc = CallBackFunc(() =>
{
    Thread.Sleep(5000);
    return DateTime.Now.Year;
});

// 这里不阻塞,可以继续做其他事
Console.WriteLine("继续执行其他任务...");

// 需要结果时才等待
int result = asyncFunc.Invoke();

⚠️ .NET Core 中 Delegate.BeginInvoke / EndInvoke 已不再支持,不能用于异步执行委托,需要用上述方式或 Task 替代。


四、ThreadPool 线程池

为什么需要线程池

Thread 的问题:

  • API 繁多,控制复杂
  • 线程数量需要程序员手动管理,容易滥用
  • 频繁创建/销毁线程,系统开销大

ThreadPool 的优势:

  • .NET Framework 2.0 引入,采用池化思想
  • 预先创建线程放入池中,需要时直接取用,用完自动归还
  • 自动管理线程数量,防止资源耗尽
  • API 更简洁,去掉了 Thread 中不必要的方法

申请线程执行任务

ThreadPool.QueueUserWorkItem(obj =>
{
    DoSomething(obj.ToString());
}, "参数");

线程等待:ManualResetEvent

ThreadPool 没有 Join(),需要用 ManualResetEvent 实现等待。

ManualResetEvent resetEvent = new ManualResetEvent(false); // 初始为未触发

ThreadPool.QueueUserWorkItem(obj =>
{
    DoSomething(obj.ToString());
    resetEvent.Set(); // 任务完成,发出信号
}, "Richard");

// 主线程可以继续做其他事
Console.WriteLine("主线程继续执行...");

resetEvent.WaitOne(); // 阻塞,直到 Set() 被调用
Console.WriteLine("子线程已完成");

工作原理:

  1. new ManualResetEvent(false) — 初始状态为"未触发"
  2. 子线程任务完成后调用 Set() — 状态变为"已触发"
  3. 主线程调用 WaitOne() — 阻塞等待,直到状态变为"已触发"才继续

控制线程数量

ThreadPool.SetMinThreads(4, 4); // 设置最小工作线程数和 IO 线程数
ThreadPool.SetMaxThreads(8, 8); // 设置最大工作线程数和 IO 线程数

ThreadPool.GetMinThreads(out int minWorker, out int minIO);
ThreadPool.GetMaxThreads(out int maxWorker, out int maxIO);
  • 第一个参数:工作线程数(Worker Threads),用于 CPU 密集型任务
  • 第二个参数:IO 线程数(Completion Port Threads),用于 IO 操作

❌ 强烈不建议手动设置线程池大小。原因:

  • 这是进程级别的全局设置
  • Task、Parallel、async/await 底层都使用线程池,修改后会影响所有这些功能
  • 设置不当容易引发性能问题甚至死锁

五、Thread vs ThreadPool 对比

特性ThreadThreadPool
出现时间.NET 1.0.NET 2.0
线程管理手动创建和销毁自动管理,复用线程
API 复杂度复杂,功能多简单,易用
性能频繁创建销毁,开销大复用线程,性能好
线程等待Join()ManualResetEvent
默认线程类型前台线程后台线程
适用场景需要精细控制的长时间任务大量短时间并发任务

六、注意事项与最佳实践

✅ 短时间任务优先使用 ThreadPool
✅ 使用 Join() 进行 Thread 的线程等待
✅ 使用 ManualResetEvent 进行 ThreadPool 的线程同步
✅ 长时间运行的线程记得设置 IsBackground = true
❌ 避免使用 Abort()、Suspend()、Resume()
❌ 不要手动设置线程池大小
❌ 不要在生产环境依赖线程优先级来控制执行顺序

⚠️ 多个线程访问共享资源时需要加锁(lock、Monitor 等),否则会出现数据竞争问题。
⚠️ 线程内部的异常不会自动传播到主线程,必须在线程内部自行处理。
⚠️ 在实际项目中,更推荐使用 Task 和 async/await,它们是对线程池的更高层封装,代码更简洁,异常处理和取消机制也更完善。

C#实战:攻克Windows平台与汇承HC-08(BLE)蓝牙模块通讯的四大核心难题 本文详细解析了在Windows平台上使用C#与汇承HC-08 BLE蓝牙模块通信时,开发者常遇到的四大核心难题及其解决方案。内容涵盖环境配置、设备扫描优化、服务特征精准解析以及读写权限判断,重点攻克了C#调用Windows BLE API与HC-08模块稳定通信的关键技术障碍。 阅读详情

相关推荐

C#基础教程08 循环

循环是C#中非常重要的编程结构,它可以让程序员重复执行特定的代码块,提高程序的运行效率。C#提供了for、while和do-while三种常用的循环结构,以及break、continue和return三种循环控制语句,可以帮助程序员更好地控制循环的执行。在编写程序时,应该选择最适合任务的循环结构,并在需要时使用循环控制语句来优化程序逻辑。

qq_45800365的博客 384

技术周总结08.12-08.18周日(C#开发环境搭建 & Linux命令)

C#开发环境搭建 & linux常用命令解析

xinkuaile的专栏 1338

Windows10开机无限蓝屏(SrtTrail.txt)

这个问题已经有很多的大佬发过解决办法了,这里仅仅作为自己日后方便查找所写文章 无法开机——自动修复失败,报SrtTrail.txt文件错误,且陷入无限蓝屏的循环 事故 故事的起因是这样的: 开开心心的组装电脑,花了几天时间来配置,然后发现USB3.0的接口太少,于是在某宝上搞到一张pcie转7口USB3.0的转接卡,往电脑上一插 插电,开机,电流轻松跑上去 然后,它和原来的磁盘阵列卡冲突,跳过阵列卡自检直接进入系统,boom,就导致了这场悲剧 解决方法 在蓝屏的界面找到:疑难解答->通过命令提示符 使

Holo__的博客 3万+

08C#事件

C#事件 1.2 事件 事件是C#语言内置的语法,可以定义和处理事件,为使用组件编程提供了良好的基础。 1.16.1 事件驱动 Windows操作系统把用户的动作都看作消息,C#中称作事件,例如用鼠标左键单击按钮,发出鼠标单击按钮事件。Windows操作系统负责统一管理所有的事件,把事件发送到各个运行程序。各个程序用事件处理函数响应事件,这种方法也叫事件驱动。 ...

weixin_30249203的博客 117

量化交易新手入门:从零开始搭建你的第一个Python交易策略(附完整代码)

本文为量化交易新手提供从零开始搭建Python双均线策略的完整指南,涵盖环境配置、数据获取、策略实现及风险管理等关键步骤。通过详细的代码示例和实践建议,帮助读者快速掌握量化交易的核心技术,并避免常见陷阱。文章特别强调Python在量化交易中的技术革新优势,适合希望进入金融科技领域的初学者。

weixin_26905487的博客 443

2024“华为杯”中国研究生数学建模竞赛(A题)深度剖析_数学建模完整过程+详细思路+代码全解析

目标是优化风力涡轮机的功率调度,使其在给定的实时风速数据下,最小化涡轮机关键组件(如塔架和主轴)的疲劳损伤。问题的挑战在于,考虑通信延迟和测量噪声的同时,要确保功率输出与风速变化的响应快速且准确。

2401_82549447的博客 6627

C#连接ABB机器人PC SDK 6.08实战指南

工业机器人上位机控制是智能制造的基础能力,其核心在于稳定、安全、可集成的通信连接。本文围绕ABB机器人PC SDK 6.08在Windows平台的工程化落地,解析网络时序约束、安全握手协议与.NET运行时适配三大关键原理;强调连接不是简单API调用,而是涵盖重试机制、状态验证、事件监听的可控控制流;技术价值体现在降低产线对示教器依赖、打通MES/OPC UA数据链路、支撑预测性维护等真实场景。内容聚焦C#开发、Windows上位机、ABB机器人连接代码等高频搜索词,为制造业工程师提供开箱即用的稳定连接封装方

weixin_31327207的博客 206

稳定性仿真(stb闭环增益仿真)

stb仿真主要是测试电路的稳定性,例如运放在闭环电路中的增益和相位裕度的值,从而判别电路是否正常工作。 以下是stb仿真的过程: (1)调用iprobe器件 从analogLib中调iprobe器件,通过器件中的箭头标识确定接入电路的方法,箭头起点端连运放的输出端,箭头的终点端连在反馈环路的作用端。iprobe器件连入电路后相当于理想导线。(iprobe器件连接在反馈回路上) (2)analyses设置 勾选stb和frequency,此处的频率范围设置在0.1~1GHz,Probe Instance/T

qq_36686804的博客 3万+

08 C# 字符串

特点描述不可变性字符串对象一旦创建,其内容就不能被更改字符类型C# 中的字符串是由字符组成的 Unicode 序列可索引性可以通过索引值访问字符串中的单个字符可迭代性可以使用 foreach 循环来迭代访问字符串中的每个字符长度属性可以使用 Length 属性获取字符串中的字符数拼接操作可以使用加号(+)实现字符串的拼接操作格式化字符串可以使用 $ 符号或者 string.Format 方法实现字符串的格式化操作转义字符。

weixin_42117570的博客 1118

手把手教你用SysMax桥接软件搞定CANoe、INCA和刷写工具(附驱动配置避坑点)

本文详细介绍了如何使用SysMax PCAN FD桥接软件实现CANoe、INCA和刷写工具的无缝对接,提供从驱动配置到多软件协同的全链路指南。重点解析了驱动安装避坑点、CANoe配置技巧、INCA集成方法以及刷写工具链优化方案,帮助汽车电子工程师构建高效开发环境。

weixin_30500105的博客 390

YOLOv8改进 | 注意力机制 | 添加DAttention (DAT)注意力机制实现极限涨点

本文给大家带来的是YOLOv8改进DAT的教程,其发布于2022年CVPR2022上同时被评选为Best Paper,由此可以证明其是一种十分有效的改进机制,其主要的核心思想是:引入可变形注意力机制和动态采样点(听着是不是和可变形动态卷积DCN挺相似)。同时在网络结构中引入一个DAT计算量由8.9GFLOPs涨到了9.4GFLOPs。本文的讲解主要包含三方面:DAT的网络结构思想、DAttention的代码复现,如何添加DAttention到你的结构中实现涨点,下面先来分享我测试的对比图。

Snu77的博客 1万+

C#进阶 08 占位符

在之前的文章里有写了一下我的第一个 C# 项目,如下: namespace _01MyFirstItem { class Program { static void Main(string[] args) { Console.WriteLine("哈哈,川哥又特么帅了"); Console.R

王川的博客 436

零基础DSP实战TMS320F28035:第九节,ADC比较器&EPWM

ADC比较器框图如下所示:-电压-----------------------------输出-电压A>电压B------------------ 1-电压A

sinat_30943509的博客 2534

C#入门记录08

结构和类 结构: 在结构声明中,除非字段被声明为const或者static, 否则无法初始化 定义: 结构修饰符 struct 结构名{ } 不支持继承 类的定义:修饰符 class 类名 类的修饰符: new : 仅允许在嵌套类声明时使用,表明类中隐藏了由基类中继承而来的、与基类中同名的成员 public: 不限制对该类的访问 p...

JamesWu9527的博客 324

在WSL2中配置代理(Windows)

通过按照上述步骤在WSL2中配置代理,我们可以轻松地在Windows子系统Linux中使用代理服务器访问互联网。请确保提供正确的代理服务器地址和端口号,并根据实际情况进行修改。在WSL2中使用代理是一个常见的需求,它允许我们在Windows子系统Linux中通过代理服务器访问互联网。本文将详细介绍如何在WSL2中配置代理,并提供相应的源代码示例。如果返回的IP地址与代理服务器所在地不同,说明代理正常工作。希望本文能对你有所帮助!如有其他问题,请随时向我提问。替换为实际的代理服务器地址和端口号。

FcUnity的博客 4355

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

c#基础08(数组)

摘要 本文介绍了C#中数组和集合的基本概念及使用方法。数组是存储相同类型元素的固定大小集合,通过索引访问元素。主要内容包括数组的声明、初始化、赋值及访问方式。集合类则提供了更灵活的数据存储结构,如ArrayList(动态数组)、Hashtable(哈希表)、Stack(堆栈)等,支持动态调整大小和多样化操作。重点讲解了ArrayList的常用属性和方法,如添加元素、排序、遍历等。文章还通过代码示例演示了数组和集合的具体操作,如循环遍历、排序输出等。

jxcklfn的博客 1174

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

C#笔记08 结构体

C#笔记08 结构体 ——本系列是基于人民邮电出版社《C#2008 C#图解教程》、清华大学出版社《C#入门经典(第五版)》两本书的自学C#笔记,如果您发现了本文的纰漏,还望不吝指正。 写在前边 *复习:类型是一种模版,C#提供了15种预定义类型,还可以创建自己的用户定义类型,有6种类型: 类类型(class) 结构类型(struct) 数组类型(array) 枚举类型(enum) 委托类型(delegate) 接口类型(interface) 1. 什么是结构体 结构体就是由几个数据组成的数据结构,这些

Tervor_HAN的博客 300

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

c# 导入excel 日期为整形如何处理?读出来是一个数值,如2008-08-08读出来是39688,怎样才能读出来是日期格式。

c# 导入excel 日期为整形如何处理?读出来是一个数值,如2008-08-08读出来是39688,怎样才能读出来是日期格式。          DateTime theDate10 = DateTime.FromOADate(double.Parse(dt_hr.Rows[i][10].ToString())); 在EXCEL中把你的日期列的格式设置一下, 设成"文本"型,就不会这样

Ai大模型,odoo,idempiere,metasfresh,低代码,ecshop数据采集发布接口,zencart,lightinthebox,shopex 数据采集接口 1190

用C#和ABB PC SDK 6.08开发Windows机器人控制台

工业机器人上位机控制本质上是通过标准化协议实现设备状态感知与指令下发的过程。其核心原理在于建立稳定会话、完成权限认证、封装底层TCP通信,并将RAPID程序生命周期管理抽象为可编程接口。C#凭借成熟的WinForms开发体验和.NET生态,成为轻量级控制终端的首选语言;而ABB PC SDK 6.08作为官方适配IRC5/OmniCore控制器的唯一稳定SDK,提供了带心跳保活、异常重连与指令校验的生产级通信能力。该技术方案显著降低MES集成门槛,适用于产线运维、多机协同调度及高校实验教学等场景,尤其适合具

weixin_33508411的博客 248

【C#进阶系列】08 方法

实例构造与引用类型 之前的章节其实已经写过了引用类型的构造过程: 首先当然是,在堆中,为引用类型的实例对象分配内存,然后初始化对象的附加字段(即类型对象指针和同步块索引)。 这个时候为对象分配的内存都是直接被置为0的,所以如果所用到的构造器中没有对对象中的一些字段做处理,那么这些字段的初始值都应该为0或者null。 如果一个类,没有构造函数,那么这个类构造的时候就会定义一个默认无...

ZQ13771472517的博客 289

C#小练习08

1一维数组二维数组字符串中整数最大值及求和 1、形参为一维整型数组,求出一维数组元素的和以及最大值。 2、形参为二维整型数组,求出二维数组元素的和以及最大值。 3、输入字符串,例如“abcd17md358fy7dfd”,求出其中整数数值的和以及最大值。 using System; using System.Collections.Generic; using System.Linq; us

哎一入江湖岁月催的专栏 1264

马上就要08年了,C#拿怎么做Codereview?

这段时间确实遇到一个很有意思的问题。众所周知,Codereview是配置管理过程当中一个相当重要的过程,对于提高代码质量来说有着相当重要的作用。上个月开始公司的过程改进已经推进到了配置管理中的CodeReview这个阶段。对此我首先是考察了各种语言下的静态代码分析软件。因为公司所用的语言比较杂,所以为了照顾大多数,C,C++有老牌的PC-lint。java下有CheckStyle。但是C#找来找去...

bilahone的博客 247

C#学习教程08——集合

C#学习教程,适合新手小白,附带学习源码。

Kint的博客 1133

C#如何调用Python:2020-11-08

C#如何调用Python:2020-11-08 C#如何调用Python:2020-11-08 C#:version2017 Python:IronPython Version2.7.10 (因为这个是基于.net4.5的) .net:version dotnet4.5 OS:windows10 step1:从github上下载最新的IronPython2.7.10 下载地址为:https://github.com/IronLanguages/ironpython2/releases/tag/ipy-2.

weixin_45858866的博客 299

C#笔记进阶篇08 泛型

C#笔记进阶篇 泛型 ——本系列是基于人民邮电出版社《C#2008 C#图解教程》、清华大学出版社《C#入门经典(第五版)》两本书的自学C#笔记,如果您发现了本文的纰漏,还望不吝指正。 写在前边: 泛型允许灵活地创建类型,处理一种或多种特定类型的对象,这些类型是在实例化时确定的,否则就使用泛型类型——清华大学出版社《C#入门经典(第五版)》 使用泛型 不定义数据类型,先写逻辑,就可以使用Object类型 可以让我们延迟定义数据类型,来编写程序 C#提供了5种泛型,类、结构、接口、委托和方法 1. 泛

Tervor_HAN的博客 593
上一篇: 12-C#
下一篇: 09-C#
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值