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

news2024/10/9 3:00:27

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【题解】【模拟】—— [NOIP2013 普及组] 表达式求值

【题解】【模拟】—— [NOIP2013 普及组] 表达式求值 [NOIP2013 普及组] 表达式求值题目背景题目描述输入格式输出格式输入输出样例输入 #1输出 #1输入 #2输出 #2输入 #3输出 #3 提示 1.简单做法1.1.题意解析1.2.AC代码 2.使用栈的做法2.1.题意解析2.2.AC代码 [NOIP2013 普及组…

C高级--shell脚本实现分支判断

题目&#xff1a; 分支结构结合test指令完成一下编程 1>判断闰年 2>输入一个数判断是否为偶数 3>使用test指令实现等级判断 90--100A 60--89B 0-50C 其他错误 代码如下&#xff1a; #!/bin/bash read -p "请输入一个年份&#xff1a;" year if [ $((y…

Cisco Meraki平台登陆

登陆以下网址 https://n4.meraki.cn/ 输入之前注册的邮箱&#xff0c;点击Next 输入之前注册时输入的密码&#xff0c;注意不是企业邮箱的密码&#xff01; 查看邮箱&#xff0c;将验证码输入&#xff0c;点击Verify&#xff08;验证&#xff09;&#xff0c;此验证码10分…

C语言-文件IO

文件IO I :input 输入&#xff0c;从文件中读取数据到内存 O:output 输出&#xff0c;把数据写入到文件 Linux系统IO 和 c语言标准IO 1、linux系统IO 1.1 简介 linux操作系统把对文件的操作封装成了多个函数&#xff0c;统称为linux系统IO。 文件描述符(File descirptor)…

笔试算法day01

目录 1.除2 2.Fibonacci数列&#xff08;Fib 数列&#xff09; 3.单词搜索 1.除2 除2&#xff01; (nowcoder.com) 算法思路&#xff1a; 只需要对最大的n个偶数进行/2即可。 将所有的偶数存进大根堆中&#xff0c;执行k次操作即可 #include <iostream> #include <…

2024年AI知识库哪家强?8款主流软件对比分析

在当今这个信息爆炸的时代&#xff0c;如何高效地管理、搜索和共享知识成为了一个重要的问题。AI知识库作为一种先进的解决方案&#xff0c;正受到越来越多企业和个人的青睐。本文将对比分析8款主流的AI知识库软件&#xff0c;帮助大家找到最适合自己的工具。 1. HelpLook AI知…

机器学习K近邻算法——回归问题K近邻算法示例

针对“数据4.1”&#xff0c;讲解回归问题的K近邻算法&#xff0c;以V1&#xff08;营业利润水平&#xff09;为响应变量&#xff0c;以V2&#xff08;固定资产投资&#xff09;、V3&#xff08;平均职工人数&#xff09;、V4&#xff08;研究开发支出&#xff09;为特征变量。…

Flutter 进阶:根据IP地址判断用户国家/地区

在应用开发中根据IP地址判断用户国家/地区的两种方法 引言 在开发国际化应用时&#xff0c;了解用户的地理位置至关重要。这不仅影响用户体验&#xff0c;还关系到内容展示和合规性。本文将介绍两种通过IP地址判断用户所在国家或地区的方法。 方法一&#xff1a;使用 ip-api…

redis高级(面试题二)

目录 一、redis的五大数据结构有哪些&#xff1f;zset底层是什么结构&#xff1f; 1、redis五大数据结构有哪些&#xff1f; 2、什么是skiplist&#xff1f; 3、zset底层是什么结构&#xff1f; 二、Redis的内存过期策略是什么&#xff1f;Redis的内存淘汰策略有哪些&#…

【专题】数据库系统的基本原理

1. 数据库系统概述 1.1. 数据库系统的应用 电信业、银行业、金融业、销售业、联机的零售商、大学、航空业、人力资源、制造业等等。 1.2 数据库系统的概念 (1) 数据&#xff08;Data&#xff09; 数据是数据库存储的基本对象。是描述现实世界中各种具体事物或抽象概念的、可…

Nuxt日志监控(服务端及客户端日志检测)

此文章主要讲解如何使用Nuxt进行日志监控&#xff0c;例如服务端请求日志&#xff0c;客户端请求日志&#xff0c;方便线上出现问题能及时排查问题所在 一、下载依赖 npm install winston winston-daily-rotate-file二、plugin下创建日志处理插件winston.js&#xff0c;对日志…

靠谱!有了它,微信自动统计报表轻松搞定!

当你需要定期统计多个微信号的数据时&#xff0c;每次都要逐一登录并手动统计各种数据&#xff0c;这不仅耗时&#xff0c;还容易出错。 好在&#xff0c;一个便捷的工具——个微管理系统能够帮助我们高效地管理这些繁杂的数据&#xff0c;让我们的工作事半功倍。 好友统计报…

安装Node.js环境,安装vue工具(最佳实践)

一、安装Node.js 去官网下载自己需求的安装包&#xff08;我提供的步骤是windows10 64x&#xff09; 下载 | Node.js 中文网 (nodejs.cn)https://nodejs.cn/download/ 下载好后&#xff0c;安装到默认路径就好了&#xff0c;所占用的内容很少。 一直点next就行了 安装好后&a…

python操作.docx、.pptx文件

python操作.docx、.pptx文件 .docx文件和.pptx文件是Microsoft Office套件中两种常见的文件格式&#xff0c;分别对应Word文档和PowerPoint演示文稿。WPS Office完美支持Microsoft Office文件格式。 使用 Python 操作 .docx 和 .pptx 文件是一项非常实用的技能&#xff0c;尤…

BlabkForestLabs 又放大招:“蓝莓”模型其实是 Flux1.1?!

神秘的 AI 生成模型 BlabkForestLabs 又放大招了&#xff1f;就在 AI 绘画圈还在训练 Flux.1 练的不亦乐乎的时候&#xff0c;黑森林工作室又推出了一个新的模型—— Flux1.1 &#xff01;这次升级后的 Flux1.1 性能直接完爆前版的 Flux.1 &#xff0c;再次将 AI 绘画的上限拉高…

如何给ppt增加新的一页?这2个ppt使用技巧值得推荐!

在当今讲究视觉表现力的时代&#xff0c;PPT已经成为职场中不可或缺的工具。无论是汇报工作、演示方案还是传递想法&#xff0c;一份精美的PPT都能让你的演讲&#xff08;演示&#xff09;更加出色。 然而&#xff0c;制作PPT并非易事&#xff0c;尤其是对于新手来说&#xff…

STM32-HAL库 驱动DS18B20温度传感器 -- 2024.10.8

目录 一、教程简介 二、驱动理论讲解 三、CubeMX生成底层代码 四、Keil5编写代码 五、实验结果 一、教程简介 本教程面向初学者&#xff0c;只介绍DS18B20的常用功能&#xff0c;但也能满足大部分的运用需求。跟着本教程操作&#xff0c;可在10分钟内解决DS18b20通信难题。…

windows认证

本地环境用户信息存储在%systemroot%/system32/SAM 域环境用户信息存储在ntds.dit 本地认证 windows系统下哈希结构&#xff1a;username:RID:LM-HASH:NT-HASH LM哈希 算法&#xff1a; 转大写&#xff0c;转二进制&#xff0c;补0补足14字节 二分获得两段字串&#xff…

算法:238.除自身以外数组的乘积

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;前缀和&#xff09; 这道题非常类似 724. 寻找数组的中心下标 在前一篇博客讲解了该题目 传送门:算法&#xff1a;724.寻找数组的中心下标 这道题目的区别在于&#xff0c;这道题是预处理前缀积和后缀积 另外&#x…

了解网页 blob 链接

blob 链接 自从 HTML5 提供了 video 标签&#xff0c;在网页中播放视频变得非常简单&#xff0c;只要在代码中插入一个 video 标签&#xff0c;再将 video 标签的 src 属性设置为视频的链接就可以了。由于 src 指向的是视频文件真实的地址&#xff0c;所以当我们通过浏览器的调…