C#多线程开发详解

news2025/2/24 11:28:07

C#多线程开发详解

  • 持续更新中。。。。。
  • 一、为什么要使用多线程开发
    • 1.提高性能
    • 2.响应性
    • 3.资源利用
    • 4.任务分解
    • 5.并行计算
    • 6.实时处理
  • 二、多线程开发缺点
    • 1.竞态条件
    • 2.死锁和饥饿
    • 3.调试复杂性
    • 4.上下文切换开销
    • 5.线程安全性
  • 三、多线程开发涉及的相关概念
    • 常用概念
      • (1)lock
      • (2)查看当前工作线程信息
    • 1.Thread(线程)
      • (1)创建线程
      • (2) 线程同步
      • (3)线程异步,并加上耗时操作
    • 2.ThreadPool(线程池)
    • 3.Task(任务)
      • (1)Task与Thead的关系
    • 4.Task Parallel Library (TPL)(任务并行库)
    • 5.Async/Await(异步/等待)
    • 6.Monitor(监视器)
    • 7.Semaphore(信号量)
    • 8.SemaphoreSlim
    • 9.AutoResetEvent(自动复位事件)
    • 10.ManualResetEvent(手动复位事件)
    • 11.CancellationToken(取消标记)
    • 12.volatile(易失性修饰符)
    • 13.Mutex(互斥锁)
    • 14.ReaderWriterLock(读写锁)
    • 15.ReaderWriterLockSlim(轻量级读写锁)
    • 16.SpinLock
    • 17.SpinWait
    • 18.Barrier(屏障)
  • 四、多线程的异常捕获问题

持续更新中。。。。。

一、为什么要使用多线程开发

1.提高性能

多线程允许程序同时执行多个任务,从而有效利用多核处理器,加快程序的执行速度。特别是在需要处理大量计算、I/O 操作或并行任务的应用中,多线程可以显著提高性能。

2.响应性

多线程使应用能够同时处理多个用户请求或事件,提高了应用的响应性。例如,多线程可以保持用户界面的响应,即使在执行长时间操作时也能让用户继续交互。

3.资源利用

多线程可以更有效地利用系统资源,如内存和网络连接。这对于高并发服务器、网络应用和数据处理任务特别有用。

4.任务分解

将复杂任务分解为多个小任务,每个任务在不同的线程中执行,可以简化问题并提高可维护性。

5.并行计算

多线程可以用于并行计算,例如在科学计算、数据分析和图像处理领域。这有助于加速大规模计算。

6.实时处理

在实时系统中,多线程可以保证任务在规定的时间内完成,从而满足对时间敏感性的需求。

二、多线程开发缺点

1.竞态条件

多线程可能会导致竞态条件,即多个线程竞争访问共享资源,可能导致数据不一致性和错误。

2.死锁和饥饿

不正确的线程同步可能导致死锁(多个线程无法继续执行)或饥饿(某些线程无法获取所需资源)问题。

3.调试复杂性

多线程程序的调试和错误跟踪可能会更加复杂,因为线程间的交互和排错可能变得更难。

4.上下文切换开销

上下文切换(Context Switching)是多线程环境中的一种操作,指的是在一个 CPU 核心上切换正在执行的线程,从当前线程的执行上下文(包括寄存器状态、程序计数器等)切换到另一个线程的执行上下文, 线程的切换需要额外的开销,因此在某些情况下,过多的线程可能会导致性能下降。

  1. 当一个线程的时间片(时间片轮转调度算法)用完,操作系统需要挂起该线程并切换到另一个线程。
  2. 当一个线程主动放弃 CPU,例如通过调用 Thread.Sleep()、Thread.Yield() 或等待某个事件时
    3.当一个线程被高优先级的线程抢占

上下文切换的过程涉及以下步骤:

  1. 保存当前线程的上下文: 操作系统将当前线程的寄存器状态、程序计数器等信息保存到该线程的内存空间中,以便稍后能够恢复该线程的执行
    2.恢复目标线程的上下文: 操作系统从目标线程的内存空间中恢复寄存器状态、程序计数器等信息,准备让目标线程继续执行。
  2. 切换内核堆栈: 每个线程都有自己的内核堆栈,上下文切换时,操作系统会切换内核堆栈,以确保线程的隔离性。

上下文切换开销指的是从一个线程切换到另一个线程的过程中所涉及的时间和资源开销。这些开销主要包括以下几个方面:

  1. 寄存器保存和恢复: 当线程切换时,操作系统需要保存当前线程的寄存器状态,然后恢复目标线程的寄存器状态。这涉及到大量的数据拷贝和计算。
    2.内存访问: 上下文切换过程中需要频繁访问内存,包括将寄存器状态和其他上下文信息写入内存,以及从内存中读取目标线程的上下文信息。
    3.调度开销: 操作系统需要决定要切换到哪个线程,这涉及到调度算法的开销,包括选择合适的线程并进行必要的线程队列操作。
    4.TLB(Translation Lookaside Buffer)失效: 当线程切换时,虚拟内存的映射可能会发生变化,导致 TLB 缓存失效,从而增加了内存访问的开销。

上下文切换开销会影响系统的整体性能,特别是在高并发、频繁切换的情况下。因此,在设计多线程应用程序时,需要考虑如何减少上下文切换的发生,以提高程序的执行效率。一些方法包括:

  1. 使用线程池:线程池可以减少线程的创建和销毁,从而减少上下文切换的频率。
  2. 合理设置线程数量:避免创建过多线程,以减少不必要的上下文切换。
    3.使用异步编程模型:使用异步操作和任务可以减少线程的使用,从而减少上下文切换。

5.线程安全性

多线程编程需要谨慎处理线程安全性,以避免数据竞争和共享资源的冲突。

三、多线程开发涉及的相关概念

常用概念

(1)lock

在 C# 中,lock 关键字用于实现线程同步,以确保在多线程环境中对共享资源的访问是安全的。lock 关键字会创建一个互斥锁(也称为监视器锁),只有一个线程可以获得该锁,从而确保在同一时间只有一个线程能够执行被 lock 包围的代码块。

lock (lockObject)
{
    // 在这里执行需要同步的代码
}

其中,lockObject 是一个用于同步的对象。它可以是任何引用类型的对象,但通常是一个专门用于同步的对象。多个线程可以共享同一个 lockObject,并且只有一个线程能够获得锁并执行被 lock 包围的代码块。

class Program
{
    static readonly object lockObject = new object(); // 同步对象

    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            ThreadStart start = () =>
            {
                lock (lockObject)
                {
                    Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} is in the critical section.");
                    Thread.Sleep(1000);
                    Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} has exited the critical section.");
                }
            };

            Thread thread = new Thread(start);
            thread.Start();
        }

        Console.ReadKey();
    }
}

在这里插入图片描述

(2)查看当前工作线程信息

可以使用 Thread.CurrentThread 属性来获取当前正在执行的线程的信息。这个属性返回一个表示当前线程的 Thread 对象,你可以使用它来查询线程的各种属性和状态。

Thread 类还提供了 Priority 属性,允许你设置线程的优先级。然而,操作系统不一定会完全遵循线程的优先级,这取决于操作系统的调度机制。

线程可以分为前台线程和后台线程。前台线程是主线程的一部分,如果所有前台线程都完成,程序将终止。后台线程是在后台运行的线程,如果所有前台线程都完成,程序会立即终止,不会等待后台线程完成。

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread currentThread = Thread.CurrentThread;

        Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");
        Console.WriteLine($"Thread Name: {currentThread.Name}");
        Console.WriteLine($"Is Thread Background: {currentThread.IsBackground}");
        Console.WriteLine($"Thread Priority: {currentThread.Priority}");
        Console.WriteLine($"Thread State: {currentThread.ThreadState}");
    }
}

在这里插入图片描述

1.Thread(线程)

表示一个执行线程,用于并行执行代码。可以使用 Thread 类来创建和管理线程。线程是执行程序的最小单位,多线程编程允许程序同时执行多个任务,从而提高性能和响应性。

Thread 类是 C# 中用于线程操作的基础类之一。然而,对于更高级的线程编程需求,你可能会使用 Task、ThreadPool、异步编程模型等更高级的机制,以便更好地管理和协调多线程操作。

Thead常用方法

  1. Start(): 启动线程,使其开始执行指定的方法。
  2. Join(): 阻塞当前线程,直到目标线程完成。
  3. Abort(): 强制终止线程的执行。不建议使用,因为可能导致资源泄漏或不稳定的状态。
  4. Sleep(int millisecondsTimeout): 使当前线程休眠指定的毫秒数。
  5. IsAlive(): 返回一个布尔值,指示线程是否处于活动状态。
  6. Interrupt(): 中断线程,引发一个 ThreadInterruptedException 异常。
  7. Suspend() 和 Resume(): 已过时,不推荐使用。用于暂停和恢复线程的执行。
  8. GetDomain() 和 GetDomainID(): 获取线程所属的应用程序域和域标识符。
  9. SetApartmentState(ApartmentState state): 设置线程的单元状态,用于控制线程的COM互操作行为。
  10. GetCurrentThreadId() 和 GetDomainID(): 获取当前线程的唯一标识符。
  11. Interrupt(): 中断线程的等待状态,引发 ThreadInterruptedException 异常。
  12. Yield(): 提示系统允许其他等待线程运行。
  13. Name 和 CurrentThread.Name: 获取或设置线程的名称。
  14. SetData 和 GetData: 在线程范围内设置和获取线程本地存储数据。
  15. Start(ParameterizedThreadStart) 和 Start(ParameterizedThreadStart, Object): 启动线程并传递参数给线程方法。
  16. TrySetApartmentState(ApartmentState): 尝试设置线程的单元状态,返回是否成功。
  17. StartNew(Action) 和 StartNew(Action, CancellationToken): 使用 Task 类来启动线程。

这些方法提供了各种线程管理和操作的能力。然而,需要注意,一些方法已经过时,不推荐使用,而且一些方法可能会涉及多线程编程的复杂性,需要谨慎使用。在编写多线程应用程序时,确保仔细阅读文档并根据需求选择适当的方法。

(1)创建线程

通常,你需要传递一个方法作为线程的入口点,然后调用 Start 方法来启动线程。

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(WorkerMethod);
        thread.Start(); // 启动线程
    }

    static void WorkerMethod()
    {
        Console.WriteLine("Thread is running.");
    }
}

(2) 线程同步

在多线程环境中,线程同步是一种确保多个线程协调工作的机制。Thread 类提供了 Join 方法,允许一个线程等待另一个线程完成。这在需要等待某个线程的结果时特别有用。

using System;
using System.Threading;

class Program
{
    static void Main()
    {
    	Thread currentThread = Thread.CurrentThread;
        Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");
        Thread thread = new Thread(WorkerMethod);
        thread.Start();

        // 主线程等待子线程完成
        thread.Join();
        Console.WriteLine("Thread has finished.");
    }

    static void WorkerMethod()
    {
      	Thread currentThread = Thread.CurrentThread;
        Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");
        Console.WriteLine("Thread is running.");
        Thread.Sleep(2000); // 模拟耗时操作
    }
}

在这里插入图片描述

(3)线程异步,并加上耗时操作

using System;
using System.Threading;

class Program
{
    static void Main()
    {
    	            Thread currentThread = Thread.CurrentThread;

            Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");
            Thread thread = new Thread(WorkerMethod);
            thread.Start();

            // 主线程等待子线程完成
            //thread.Join();
            Console.WriteLine("Thread has finished.");
            //这里子线程虽然还没有处理完,但是直接返回了,没有继续等待子线程,但是子线程还在继续处理工作,没有出现阻塞现象
            return "ok";
    }

    static void WorkerMethod()
    {
      	 Thread currentThread = Thread.CurrentThread;

            Console.WriteLine($"Thread ID: {currentThread.ManagedThreadId}");
            Console.WriteLine("Thread is running.");
            Thread.Sleep(10000); // 模拟耗时操作
            //这里在主线程结束后,继续在处理10s后打印Thread is WordEnd;
            Console.WriteLine("Thread is WordEnd.");
    }
}

可以思考下,主线程返回成功了,但是子线程执行失败了,这可怎么办?

在这里插入图片描述

2.ThreadPool(线程池)

是一个用于管理和重用线程的机制,可以使用 ThreadPool 类来执行异步任务。

3.Task(任务)

表示一个异步操作,可以使用 Task 类或 Task.Run 方法来创建和管理任务。

(1)Task与Thead的关系

4.Task Parallel Library (TPL)(任务并行库)

是 C# 中用于并行编程的高级库,用于处理异步和并行操作,包括数据并行和任务并行。

5.Async/Await(异步/等待)

是 C# 5.0 引入的异步编程模型,用于创建和管理异步方法和操作。

6.Monitor(监视器)

是用于实现线程同步的一种机制,用于保护共享资源,避免竞态条件。可以使用 Monitor 类或 lock 关键字来实现。

首先lock和Minitor有什么区别呢?
其实lock在IL代码中会被翻译成Monitor。也就是Monitor.Enter(obj)和Monitor.Exit(obj).

lock(obj)
{}

//等价为:
try

{    
      Monitor.Enter(obj)
}

catch()
{}
finally
{
      Monitor.Exit(obj)
}

7.Semaphore(信号量)

用于控制并发访问资源的数量,可以使用 Semaphore 类来创建和管理信号量。

8.SemaphoreSlim

是 Semaphore 的改进版本,提供更好的性能和可伸缩性。

9.AutoResetEvent(自动复位事件)

用于线程同步,允许一个线程等待另一个线程发出信号。

10.ManualResetEvent(手动复位事件)

用于线程同步,允许一个线程等待多个线程发出信号。

11.CancellationToken(取消标记)

用于在异步操作中请求取消操作,可以在异步方法中传递给取消标记。

12.volatile(易失性修饰符)

用于标记字段,指示编译器不应该对标记字段进行优化,以确保多线程环境下的正确性。

13.Mutex(互斥锁)

是一种用于实现线程同步的机制,用于保护共享资源,防止多个线程同时访问。

14.ReaderWriterLock(读写锁)

允许多个线程同时读取共享资源,但只允许一个线程写入资源。适用于读操作频繁、写操作较少的场景。

15.ReaderWriterLockSlim(轻量级读写锁)

是 ReaderWriterLock 的改进版本,提供更好的性能和可伸缩性。

16.SpinLock

是一种自旋锁,用于短时间内的临界区保护。它使用忙等待来尝试获取锁,适用于临界区很小的情况。

17.SpinWait

用于在自旋等待期间执行自旋操作,可以根据不同的条件进行自旋。

18.Barrier(屏障)

允许多个线程在一个点上等待,直到所有线程都达到该点。适用于需要所有线程协调同步的场景。

四、多线程的异常捕获问题

相关文章

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

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

相关文章

python压缩pdf文件大小

pdf文件过大&#xff0c;经常会是一个问题&#xff0c;但是市面上基本上都是收费的工具&#xff0c;wps需要开会员才能使用。因此找了一个python库进行试验&#xff1a; 首先需要安装 pip install aspose-pdf 运行的代码&#xff1a; import aspose.pdf as apcompressPdfDo…

云原生k8s---资源限制、探针

目录 一&#xff1a;资源限制 1、资源限制原因 2、Pod 和 容器 的资源请求和限制 3、CPU 资源单位 4、内存 资源单位 5、事例 &#xff08;1&#xff09;事例一 &#xff08;2&#xff09;事例二 二&#xff1a;重启策略 1、重启策略模式 2、事例 三&#xff1a;探针…

初学HTML:在线简易画板设计。

最近在HTML&#xff0c;记录下一点点成果。 设计了一个简易画板&#xff0c;通过HTML的Canvas元素实现一个在线画板&#xff0c;用户可以在上面绘制图形或涂鸦。 下面是运行效果&#xff1a; 下面是代码&#xff1a; <!DOCTYPE html> <html> <head><ti…

leetcode1052. 爱生气的书店老板(java)

爱生气的书店老板 爱生气的书店老板题目描述滑动窗口代码演示 往期经典算法 爱生气的书店老板 难度 - 中等 原题链接 - 爱生气的书店老板 题目描述 有一个书店老板&#xff0c;他的书店开了 n 分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n 的整数数组 customers &a…

校对软件在司法系统中的应用:加强刑事文书审查

校对软件在司法系统中的应用可以加强刑事文书审查&#xff0c;提高文书的准确性和可靠性。 以下是校对软件在刑事文书审查方面的应用&#xff1a; 1.语法和拼写检查&#xff1a;校对软件可以自动检查刑事文书中的语法错误和拼写错误。这包括句子结构、主谓一致、动词形式等方面…

element-plus里,设置Message消息提示框组件显示的位置,垂直居中

element-plus里的 Message 组件&#xff0c;默认展示的位置在最顶部&#xff0c;依次往下叠加&#xff0c;如图所示 想要解决它显示的位置&#xff0c;可以使用组件里的 offset属性&#xff0c;设置 Message 距离窗口顶部的偏移量,想要垂直居中&#xff0c;可以这么设置&#…

猿辅导竞赛老师成为“最强辅助”,在线揭秘数学新生代的成长故事

7月12日&#xff0c;2023年第64届IMO闭幕式在日本千叶幕张国际展览中心举行&#xff0c;中国队以6枚金牌&#xff0c;240分的成绩获得世界第一。本届IMO金牌线为32分&#xff0c;共53人获得金牌&#xff1b;银牌线为25分&#xff0c;共89人获得银牌&#xff1b;铜牌线为18分&am…

资讯速递 | ArkUI-X 预览版已正式开源!

OpenHarmony项目群技术指导委员会&#xff08;以下简称“TSC”&#xff09;-跨平台应用开发框架TSG所孵化项目 —— ArkUI-X&#xff0c;近期已正式开源 &#xff0c;开发者基于一套主代码&#xff0c;就可以将在OpenHarmony上开发的精美、高性能应用同时运行在Android、iOS等其…

【论文阅读】基于深度学习的时序预测——Pyraformer

系列文章链接 论文一&#xff1a;2020 Informer&#xff1a;长时序数据预测 论文二&#xff1a;2021 Autoformer&#xff1a;长序列数据预测 论文三&#xff1a;2022 FEDformer&#xff1a;长序列数据预测 论文四&#xff1a;2022 Non-Stationary Transformers&#xff1a;非平…

Springboot配置多个redis连接

1. 添加Redis依赖 首先&#xff0c;打开您的Spring Boot 项目的 pom.xml 文件并添加以下 Redis 相关依赖&#xff1a;<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></de…

方案展示 | RK3588开发板Android12双摄方案

iTOP-RK3588开发板使用手册更新&#xff0c;后续资料会不断更新&#xff0c;不断完善&#xff0c;帮助用户快速入门&#xff0c;大大提升研发速度。 ​RK3588开发板载4路MIPI CAMERA摄像头接口、MIPI CSI DPHY的4.5Gbps、2.5Gops的MIPI CSI CPHY&#xff0c;四路同时输入&#…

【杂谈】电脑城的消失:时代变迁中的一段峡谷

导语&#xff1a;在过去很长一段时间里&#xff0c;想要购买电子设备都逃不开一个叫“电脑城”的地方&#xff0c;那里鱼龙混杂良莠不齐&#xff0c;是令许多人记忆深刻分外难忘之处。但是随着时代发展电商兴起&#xff0c;采用传统线下销售的电脑城却逐渐衰退甚至面临消失&…

【BASH】回顾与知识点梳理(二十二)

【BASH】回顾与知识点梳理 二十二 二十二. Linux 账号管理22.1 Linux 的账号与群组使用者标识符&#xff1a; UID 与 GID使用者账号/etc/passwd 文件结构/etc/shadow 文件结构 关于群组&#xff1a; 有效与初始群组、groups, newgrp/etc/group 文件结构有效群组(effective grou…

班费管理系统(Mybatis + Swing)学生班级信息java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 班费管理系统&#xff08;Mybatis Swing) 系统有1权…

Java 正则表达式【匹配与分组基本原理】

简介 我们一般使用正则表达式是用来处理字符串的&#xff0c;不管是实际的开发中还是我们的算法竞赛中&#xff0c;使用正则表达式绝对可以大大提升我们的效率。 正则表达式&#xff08;regular expression&#xff09;其实就是对字符串进行模式匹配的技术。 快速入门 我们这里…

要过软考,务必先搞清楚这3点

前段时间软考成绩出来了&#xff0c;大家都过了没&#xff1f; 我看好多人都说早上的题目稳过&#xff0c;下午的好多都挂了。 软考每年这个通过率&#xff0c;确实是一言难尽。 到底怎么样才能过&#xff0c;自学、培训&#xff0c;各种诀窍&#xff0c;五花八门。 但考不过…

【无标题杭州生物制药公司【阿诺医药】申请纳斯达克IPO上市】

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;杭州生物制药公司阿诺医药&#xff08;Adlai Nortye&#xff09;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为&am…

OLED透明屏,与传统的显示屏相比,有哪些特点和优势?

近年来&#xff0c;随着科技的不断发展&#xff0c;人们对于安全防护的需求也越来越高。 传统的安防设备已经不能满足人们的需求&#xff0c;因此&#xff0c;透明屏作为一种新型的安防展示设备&#xff0c;逐渐受到人们的关注和喜爱。 透明屏是一种将显示屏与玻璃结合的新型…

要求表里某字段值唯一

在开发过程中&#xff0c;要求表里某字段值唯一 一、场景 在单据&#xff0c;要求某字段值不重复 查看数据模型&#xff1a; 查看单据&#xff1a; 二、问题 区域编码&#xff0c;区域名称不重复 三、解决方案 1&#xff09;数据库加索引 2&#xff09;书写保存后存储过…

plt绘制雷达图

1、圆形 import matplotlib.pyplot as plt import numpy as npplt.rcParams[font.sans-serif][SimHei] #显示中文 plt.rcParams[axes.unicode_minus]False #正常显示负号def radar_map(data, label, cls):# 设置雷达图的角度&#xff0c;用于平分切开一个圆面n len…