C#多线程基本使用和探讨

线程是并发编程的基础概念之一。在现代应用程序中,我们通常需要执行多个任务并行处理,以提高性能。C# 提供了多种并发编程工具,如ThreadTask、异步编程和Parallel等。

Thread 类

Thread 类是最基本的线程实现方法。使用Thread类,我们可以创建并管理独立的线程来执行任务。

基本使用Thread 

创建一个新的实例对象,将一个方法直接给Thread类,并使用实例对象启动线程运行。

   static void Test() {Console.WriteLine("Test事件开始执行");Thread.Sleep(1000);Console.WriteLine("Test事件睡眠之后打印数据");Console.WriteLine("Test事件id: " + Thread.CurrentThread.ManagedThreadId);}static void Main(string[] args){#region //主线程idConsole.WriteLine("主线程id: " + Thread.CurrentThread.ManagedThreadId);#endregion#region //传递一个方法给线程,执行方法Thread t = new Thread(Test);t.Start();#endregion
}

线程数据传输

传递单个参数

Thread 提供了一个 Start(object parameter) 方法,可以在启动线程时传递一个参数。

static int Downlod(object obj) {string str = obj as string;Console.WriteLine(str);}static void Main(string[] args){Thread t1 = new Thread(Downlod);//这里传递的参数是字符串t1.Start("数据传递方法执行 ,数据传递方法id: "+Thread.CurrentThread.ManagedThreadId);}

这种方式适用于需要传递单个参数的情况。

使用类的实例方法传递多个参数

先new一个类的实例,给实例字段赋值,调用类的实例,通过实例调用方法,构造函数接收参数,然后在线程中调用该实例的方法。

     static void Main(string[] args){
// 使用 DownloadTool 实例化并传递数据
DownloadTool downloadTool = new DownloadTool("www.baidu.com", "这是下载链接哦");
Thread thread = new Thread(downloadTool.Download);
thread.Start();
}public class DownloadTool
{private string url;private string message;public DownloadTool(string url, string message){this.url = url;this.message = message;}public void Download(){Console.WriteLine("下载链接: " + url);Console.WriteLine("提示信息: " + message);Console.WriteLine("Download 线程 ID: " + Thread.CurrentThread.ManagedThreadId);}
}

thread 线程启动时,它会执行 downloadTool.Download() 方法,输出传递的数据。

线程优先级 

在 C# 中,可以使用 Thread.Priority 属性来设置线程的优先级。线程优先级决定了操作系统在多线程环境中调度线程的顺序,但并不保证高优先级的线程总是比低优先级的线程更早或更频繁地执行。

线程优先级级别

C# 提供了五个线程优先级级别,定义在 ThreadPriority 枚举中:

  1. Lowest:最低优先级。操作系统尽可能少地调度这个优先级的线程。
  2. BelowNormal:低于正常的优先级。优先级比 Normal 低,但高于 Lowest。
  3. Normal:默认优先级,大多数线程默认的优先级。适用于一般用途。
  4. AboveNormal:高于正常的优先级。操作系统更倾向于调度这个优先级的线程。
  5. Highest:最高优先级。操作系统尽可能多地调度这个优先级的线程。
  internal class Program{static void A(){int i = 0;while (true){i++;Console.WriteLine($"A 输出第{i}");Thread.Sleep(1000);}}static void B(){int i = 0;while (true){i++;Console.WriteLine($"B 输出第{i}");Thread.Sleep(1000);}}static void Main(string[] args){//在C#中,线程的优先级可以通过Thread.Priority属性来设置和获取。
//        Lowest: 线程的优先级是最低的。在系统中存在其他活动线程时,此优先级的线程很少得到执行。
//BelowNormal: 线程的优先级低于正常线程。
//Normal: 线程的优先级是普通的,这是线程的默认优先级。
//AboveNormal: 线程的优先级高于正常线程。
//Highest: 线程的优先级是最高的。此优先级的线程会尽量优先于其他所有优先级的线程执行。Thread a = new Thread(A);Thread b = new Thread(B);a.Priority = ThreadPriority.Highest;a.Start();b.Priority = ThreadPriority.Lowest;b.Start();Console.WriteLine("按任意键停止线程...");Console.ReadKey();a.Join();b.Join();Console.WriteLine("线程已停止");}}
  • A 被设置为最高优先级 ThreadPriority.Highest
  • B 被设置为最低优先级 ThreadPriority.Lowest

注意事项

  1. 优先级不是绝对控制:操作系统可能会忽略优先级设置,特别是在资源有限的系统中。高优先级线程不一定会一直执行,也不能阻止低优先级线程的执行。

  2. 使用优先级的适用场景:设置线程优先级可能适用于实时系统(例如,某些任务需要优先处理)。但是,大多数应用程序通常可以使用默认的 Normal 优先级。

  3. 避免使用过多的高优先级线程:如果所有线程都被设置为 Highest,系统的整体性能可能会下降,甚至导致线程争用 CPU 资源的情况。

  4. CPU 密集型任务:在 CPU 密集型任务中,优先级可能会对性能产生较大影响,因为优先级高的线程可能会占用更多的 CPU 时间。

线程优先级的最佳实践

  • 默认使用 Normal 优先级,除非有特殊原因。
  • 避免滥用 Highest 优先级,因为它会对系统资源产生影响。
  • 在 I/O 密集型的线程中,优先级通常不会有显著差异,因为这些线程在等待 I/O 操作完成时,CPU 会调度其他线程。

通过合理设置线程优先级,可以帮助操作系统更好地调度线程,以满足应用程序的需求。 但通常在 .NET 应用程序中,多数情况下使用默认的 Normal 优先级就足够了。

线程池

线程池 (ThreadPool) 是一种高效的管理和调度线程的方式。线程池自动管理线程的创建、重用和销毁,从而减少了手动创建和管理线程的开销。

为什么使用线程池

  1. 性能更高:线程池会重用现有的线程,减少了创建和销毁线程的开销。
  2. 自动管理:线程池会根据系统负载动态调整线程数量。
  3. 避免线程资源不足:线程池限制了同时运行的线程数,避免了线程过多导致的资源耗尽问题。

基本使用 ThreadPool.QueueUserWorkItem 方法将任务排入线程池队列。

using System;
using System.Threading;class Program
{static void Main(string[] args){// 将任务排入线程池ThreadPool.QueueUserWorkItem(DoWork, "任务 1");ThreadPool.QueueUserWorkItem(DoWork, "任务 2");Console.WriteLine("主线程完成");Thread.Sleep(3000); // 等待线程池中的任务完成}static void DoWork(object state){string taskName = (string)state;Console.WriteLine($"{taskName} 开始执行 - 线程ID: {Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(1000); // 模拟耗时操作Console.WriteLine($"{taskName} 执行完成 - 线程ID: {Thread.CurrentThread.ManagedThreadId}");}
}

运行结果QueueUserWorkItem自动分配线程来执行任务。

QueueUserWorkItem 方法将任务排入线程池。它接收一个委托(即方法)和一个可选的状态对象(传递给方法的数据)。

DoWork 方法接受一个参数 state,这是从 QueueUserWorkItem 传递的。

Thread.Sleep(3000) 确保主线程不会立即退出,使得线程池中的任务有机会完成。

使用带返回值的线程池任务

C# 中的 ThreadPool 通常不直接支持返回值。如果需要获得任务结果,可以使用 Task,因为 Task 本质上也是线程池的一部分。Task 更适合于带返回值的异步操作。这里使用 Task.Run 来代替 ThreadPool

     static void Main(string[] args){//使用tesk多线程int a= Task.Run(() =>{int a =  Dowload();return a;}).Result;Task<int> task = Task<int>.Run(()=>{int a =   Dowload();return a;});//初始化一个CancellationTokenSource实例CancellationTokenSource source = new CancellationTokenSource();//task.Start();task.Wait(1000);source.Cancel();int result  =    task.Result;Console.WriteLine(result);Console.WriteLine($"tesk返回值{a}");
}static int  Dowload() {int a = 0;for (int i = 0; i < 10; i++){a=  a + i + 1;}int?  id= Task.CurrentId;Console.WriteLine("Current thread ID: " + id);return a;}

线程池的限制

  • 任务运行时间过长:线程池中的线程本质上是共享资源,如果某个任务运行时间太长,将会占用线程池中的线程,导致其他任务无法及时执行。
  • 不适合实时系统:线程池中的任务调度是由系统管理的,无法保证精确的实时性。
  • 有限的线程数量:在高并发场景中,如果线程池中的线程全部被占用,新的任务将会等待,直到有线程可用。
线程池总结

线程池是一种高效的并发处理方式,适合于大多数轻量级的后台任务。在现代 C# 编程中,建议使用 Taskasync/await 进行异步操作,因为它们能简化代码,并且使用底层的线程池来管理线程。如果需要精确控制线程的执行,通常建议使用手动管理的 Thread 等。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/442596.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读笔记-XLNet: Generalized Autoregressive Pretraining for Language Understanding

前言 Google发布的XLNet在问答、文本分类、自然语言理解等任务上都大幅超越BERT,XLNet提出一个框架来连接语言建模方法和预训练方法。我们所熟悉的BERT是denoising autoencoding模型,最大的亮点就是能够获取上下文相关的双向特征表示,所以相对于标准语言模型(自回归)的预…

【基础算法总结】字符串篇

目录 一&#xff0c;算法简介二&#xff0c;算法原理和代码实现14.最长公共前缀5.最长回文子串67.二进制求和43.字符串相乘 三&#xff0c;算法总结 一&#xff0c;算法简介 字符串 string 是一种数据结构&#xff0c;它一般和其他的算法结合在一起操作&#xff0c;比如和模拟&…

【算法笔记】二分算法原理的深度剖析

【算法笔记】二分算法原理的深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;算法笔记 文章目录 【算法笔记】二分算法原理的深度剖析前言一.二分查找1.1题目1.2朴素二分1.3细节问题1.4代码实现1.5朴素模版总结 二.在排序数组中查找…

Rust编程的匹配控制语句match

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 学过C语言的同学或许在等switch&#xff0c;明确告诉你们&#xff0c;Rust没有switc…

Jenkins打包,发布,部署

一、概念 Jenkins是一个开源的持续集成工具&#xff0c;主要用于自动构建和测试软件项目&#xff0c;以及监控外部任务的运行。与版本管理工具&#xff08;如SVN&#xff0c;GIT&#xff09;和构建工具&#xff08;如Maven&#xff0c;Ant&#xff0c;Gradle&#xff09;结合使…

Android Studio实现安卓心理健康咨询

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 项目代号161 1.开发环境 android stuido3.6 jdk1.8 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.心理测评 3.测评结果 4.心理咨询预约 5.心理综合辅导 6.个人中心 7.历史咨…

知识图谱入门——11:构建动态图谱渲染应用:Vue3与Neo4j的集成与实践

在知识图谱与大数据技术领域&#xff0c;构建动态图谱是一项非常重要的任务。这篇博客将带你深入了解如何利用Vue.js、D3.js以及Neo4j&#xff0c;开发一个能够实时渲染图谱节点和关系的应用。我们将从零开始&#xff0c;介绍如何搭建开发环境、安装依赖、与Neo4j数据库交互、到…

考研笔记之操作系统(三)- 存储管理

操作系统&#xff08;三&#xff09;- 存储管理 1. 内存的基础知识1.1 存储单元与内存地址1.2 按字节编址和按字编址1.3 指令1.4 物理地址和逻辑地址1.5 从写程序到程序运行1.6 链接1.6.1 静态链接1.6.2 装入时动态链接1.6.3 运行时动态链接 1.7 装入1.7.1 概念1.7.2 绝对装入1…

分支预测器BPU

分支预测器BPU 0 Intro0.1 CPU执行过程0.2 分支预测0.2.1 TAGE预测器0.2.2 跳转地址 分支预测器BPU是深入研究一个高性能处理器的一个很好的开始项目&#xff1b; 0 Intro 条件分支是指后续具有两路可执行的分支。可以分为跳转分支(taken branch)和不跳转分支(not-taken branc…

ES创建文档,使用postman调用请求

请求的url 地址是http://192.168.1.108:9200/shopping/_doc&#xff0c;请求方式为post, 请求参数为: { "title":"小米手机", "category":"小米", "images":"http://www.gulixueyuan.com/xm.jpg", "price&…

IDEA 编译报错 “java: 常量字符串过长” 的解决办法

目录 一、问题描述二、问题原因2.1 理论角度2.2 源码角度 三、解决方案解决方案①&#xff1a;StringBuilder 拼接解决方案②&#xff1a;读取文件内容 四、方案验证 在线文本换行工具&#xff1a; https://lzltool.cn/Toolkit/WrapWordsInText 一、问题描述 今天在开发过程中…

CPU、GPU、显卡

CPU VS GPUCPU&#xff08;Central Processing Unit&#xff09;&#xff0c;中央处理器GPU&#xff08;Graphics Processing Unit&#xff09;&#xff0c;图形处理单元GPU 的技术演变CUDA&#xff08;Compute Unified Device Architecture&#xff09; 显卡&#xff08;Video…

016 规格参数

文章目录 新增AttrController.javaAttrVo.javaAttrServiceImpl.javaAttrAttrgroupRelationEntity.javaAttrEntity.javaAttrGroupEntity.java 查询AttrController.javaAttrServiceImpl.javaAttrRespVo.java 修改回显AttrController.javaAttrServiceImpl.java 修改提交AttrContro…

Word 插入表格的具体步骤图解

Word 是工作和学习中比较常用的软件之一&#xff0c;有时候在使用的过程中可能需要插入一个表格来整理一些数据&#xff0c;但是有的人可能不知道如何插入表格&#xff0c;下面就给大家总结了 Word 怎么插入表格。 Word 插入表格 Word 插入表格之后可以在里面填写数据和文本&…

时序约束进阶四:set_input_delay和set_output_delay详解

目录 一、前言 二、set_input_delay/set_output_delay 2.1 延时约束 2.2 约束设置界面 2.3 示例工程 2.4 Delay Value 2.5 Delay value is relative to clock edge 2.6 Delay value already includes latencies of the specified clock edge 2.7 Rise/Fall 2.8 Max/M…

更新C语言题目

1.以下程序输出结果是() int main() {int a 1, b 2, c 2, t;while (a < b < c) {t a;a b;b t;c--;}printf("%d %d %d", a, b, c); } 解析:a1 b2 c2 a<b 成立 ,等于一个真值1 1<2 执行循环体 t被赋值为1 a被赋值2 b赋值1 c-- c变成1 a<b 不成立…

使用IOT-Tree Server制作一个边缘计算设备(Arm Linux)

最近实现了一个小项目&#xff0c;现场有多个不同厂家的设备&#xff0c;用户需要对此进行简单的整合&#xff0c;并实现一些联动控制。 我使用了IOT-Tree Server这个软件轻松实现了&#xff0c;不外乎有如下过程&#xff1a; 1&#xff09;使用Modbus协议对接现有设备&#…

9-贪心算法

PDF文档下载&#xff1a;LeetCode-贪心算法-java 参考&#xff1a;代码随想录 题目分类大纲如下&#xff1a; 贪心算法理论基础 什么是贪心&#xff1f; 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心的套路&#xff08;什么时候用贪心&#xff…

C++ STL容器(五) —— priority_queue 底层剖析

这篇来讲下 priority_queue&#xff0c;其属于 STL 的容器适配器&#xff0c;容器适配器是在已有容器的基础上修改活泼限制某些数据接口以适应更特定的需求&#xff0c;比如 stack 栈使数据满足后进先出&#xff0c;queue 队列使数据满足先进先出&#xff0c;其都是在已有容器上…

转型AI产品经理需要掌握的硬知识、经理能力模型和常见AI概念梳理

近几年&#xff0c;从亚马逊&#xff0c; Facebook&#xff0c;到谷歌&#xff0c;微软&#xff0c;再到国内的BAT&#xff0c;全球最具影响力的技术公司都将目光转向了人工智能&#xff08; AI &#xff09;。2016年 AlphaGo 战胜李世石&#xff0c;把公众的目光也聚集到了人工…