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

devtools/2024/10/9 11:08:04/

线程是并发编程的基础概念之一。在现代应用程序中,我们通常需要执行多个任务并行处理,以提高性能。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.ppmy.cn/devtools/123340.html

相关文章

如何通过Kubectl 重启Pod的六种方法

大家可能都知道 kubectl 其实没有 restart pod 这个命令&#xff0c;这个主要是由于在 k8s 中pod 的管理属于rs 等控制器&#xff0c;并不需要运维手动维护&#xff0c;但有时候&#xff0c;我们修改了configmap 的配置文件后&#xff0c;希望重启pod 加载配置&#xff0c;此时…

Unity3D游戏的内存控制详解

Unity3D是一款流行的游戏引擎&#xff0c;支持多种平台&#xff0c;包括PC、移动设备和VR等。随着游戏的复杂性不断提高&#xff0c;Unity3D的内存管理变得尤为重要。本文将详细介绍Unity3D游戏中的内存控制技术&#xff0c;包括自动内存管理、对象池、延迟加载资源和手动清理资…

【Power Compiler手册】13.UPF多电压设计实现(13)

井偏置支持 一些工艺技术允许将专用的电压供电,而不是常规的轨电压,应用于芯片的n阱和p阱区域。对阱施加偏置电压会改变阱中晶体管的阈值电压,从而影响性能和漏电流。 Power Compiler工具提供了一个可选模式,使用UPF命令指定n阱和p阱偏置供电基础设施。在此模式下,工具会…

低质量数据的多模态融合方法

目录 多模态融合 低质量多模态融合的核心挑战 噪声多模态数据学习 缺失模态插补 平衡多模态融合 动态多模态融合 启发式动态融合 基于注意力的动态融合 不确定性感知动态融合 论文 多模态融合 多模态融合侧重于整合多种模态的信息,以实现更准确的预测,在自动驾驶、…

STM32—SPI通讯协议

前言 由于I2C开漏外加上拉电阻的电路结构&#xff0c;使得通信线高电平的驱动能力比较弱&#xff0c;这就会号致&#xff0c;通信线由候电平变到高电平的时候&#xff0c;这个上升沿耗时比较长&#xff0c;这会限制I2C的最大通信速度&#xff0c; 所以&#xff0c;I2C的标准模…

Linux dlsym和直接调用函数地址解析分析

dlsym 函数是 Linux 下动态链接库&#xff08;shared library&#xff09;编程中的一个重要函数。它用于在运行时获取动态链接库中符号的地址&#xff0c;通常用于获取函数指针或变量的地址。 以下是 dlsym 函数的基本用法和示例。 1. 函数原型 void *dlsym(void *handle, c…

ASP.NetCore---I18n(internationalization)多语言版本的应用

文章目录 0.实现的效果如下1.创建新项目I18nBaseDemo2.添加页面中的下拉框3.在HomeController中添加ChangeLanguage方法4.在Progress.cs 文件中添加如下代码&#xff1a;5. 在progress.cs中添加code6.添加Resource资源文件7.在页面中引用i18n的变量8. 重启项目&#xff0c;应该…

Java面试题——第九篇(JVM)

1. Java中的强引用、软引用、弱引用和虚引用分别是什么 强引用 最常见的引用类型&#xff0c;在Java中&#xff0c;默认情况下&#xff0c;任何普通的对象引用都是强引用只要一个对象有强引用指向他&#xff0c;垃圾回收器永远不会回收该对象&#xff0c;即使系统内存紧张。 …