C# 多线程

C# 多线程

基本概念

什么是线程

线程是能独立运行的最小单位,也是程序能够并发执行的一段指令序列

线程是进程的一部分,一个进程可以包含多个线程,这些线程共享进程的资源

进程有入口线程,可以创建更多的线程


为什么要多线程

重复任务希望同时进行(比如对于数组中的每个元素都进行相同且耗时的操作)

比如我处理一个数组的元素,每个都要耗时,如果我每处理一个再处理下一个,时间是n*m

但是我如果同时进行处理,时间是m

多个不同任务希望同时进行,互不干扰(比如有多个后台线程需要做轮询等操作)

比如我主线程有一个任务读取变换的数,然后我额外开一个后台线程去发送我读取的数

不然我读取变化的数进行发送,发送十分耗时,这将导致我读取不完全


什么是线程池

一组预先创建的线程,可以被重复使用来执行多个任务

一个线程的创建和销毁是十分耗时的,开销很大

当我们需要执行一个小任务时,直接找线程池要一个,做完再把线程还给他

避免频繁地创建和销毁线程,从而减少了线程创建和销毁的开销,提高了系统的性能和效率

异步编程默认使用线程池

通过异步编程可以更优雅的调用线程池,不需要我们自己去调用线程池的代码


什么是线程安全

线程安全

多个线程访问共享资源时,对共享资源的访问会导致数据不一致或产生不可预期的结果

csharp 复制代码
public class TODO {
    static int cnt = 0;
    const int total = 1000000;
    static void Main() {

        var thread1 = new Thread(foo);
        var thread2 = new Thread(foo);
        thread1.Start();
        thread2.Start();
        thread1.Join();
        thread2.Join();

        Console.WriteLine(cnt);

    }

    static void foo() {
        for(int i = 0; i < total; i++) {
            cnt++;
        }
    }
}

上述代码在两个进程同时访问一个数时,期待结果是total*2,但由于奇奇怪怪的原因,将会小于total

比如线程1线程2同时拿到cnt,同时对cnt++,这将导致其中一个线程的++是被覆盖的

(汇编解释为,我取出cnt,自增然后赋值,他们同时赋值将导致本来自增两次的值只有一次生效)

简言之,同时发生导致少加一次

可以使用lock解决此问题

同步机制

用于协调和控制多个线程之间执行顺序和互斥访问共享资源

确保线程按照特定的顺序执行,避免竞态条件和数据不一致的问题

原子操作

在执行过程中不会被中断的操作。不可分割,要么完全执行,要么完全不执行,没有中间状态

在多线程环境下,原子操作能够保证数据的一致性和可靠性,避免出现竞态条件和数据竞争的问题

只需要一步就能完成的操作,不是指一行代码,而是对于底层来说,汇编啥的,也是一步就能完成

当然也可以用提供的函数来InterLocked.foo(ref elem)来实现原子操作


常用实现方式一

线程

new 一个 thread

线程池

使用thread pool 这个类型里面的方法

异步编程

asyncawait

复制代码
asynchronization n.异步化 异步,非同步化
考虑一下自带方法
csharp 复制代码
Parallel		For、ForEach、Invoke
PLINQ  			AsParallel、AsSequential、AsOrdered

即不需要那么底层的去实现



线程Thread

线程的创建

创建Thread实例,并传入ThreadStart委托

还可以配置线程,如是否为后台线程

调用Thread.Start方法,还可以传参


线程的终止

调用Thread.Join方法,等待线程的结束

意味着谁Join,我就要等谁结束了再继续别的事

简言之,用于等待一个线程结束

csharp 复制代码
public class TODO {
    static void Main() {
        var thread = new Thread((x) => {
            Console.WriteLine("Hello, {0}", x);
            for (int i = 0; i < 10; i++) {
                Thread.Sleep(500);
                Console.WriteLine("i = {0}", i);
            }
            Console.WriteLine("finished!");
        });

        Console.WriteLine("start");
        thread.Start("Bob");
        thread.Join();
        Console.WriteLine("over");

    }
}
调用Thread.Interrupt方法,中断线程的执行

会在相应线程中抛出ThreadInterruptedException

如果线程中包含一个while(true)循环

那么需要保证包含等待方法,如IO操作Thread.Sleep

如果没有这些,那while(true)会没有空来抛出异常

就小小的阻塞他一下 thread.Sleep(0);

不能用Abort

他会直接干掉这个线程,g

使用Abort方法来强制终止线程可能导致一些严重的问题,包括资源泄漏和不可预测的行为

较新版本的.NET中如果使用这个方法,会报PlatformNotSupportedException

推荐使用Thread.InterruptCancellationToken


线程的挂起与恢复

Thread. Suspend以及Thread.Resume

你的挂起可能让线程暂停在任意一种状态,这就见鬼了

较新版本的.NET中,这两个方法已经被标记为Obsolete,且调用会报错

推荐使用锁、信号量等方式实现这一逻辑



线程安全于同步机制

原子操作

interlocked


锁与信号量

自己找例子

lock & Monitor
Mutex
Semaphore
WaitHandle

ManualResetEvent
AutoResetEvent 用于生产者消费者模型

ReaderWriterLock

轻量型

SemaphoreSlim
ManualResetEventSlim
ReaderWriterLockSlim

不要自己造轮子

线程安全的单例

Lazy

线程安全的集合类型

ConcurrentBag、

ConcurrentStack、ConcurrentQueue、

ConcurrentDictionary

阻塞集合

BlockingCollection

通道

Channel

原子操作

Interlocked

周期任务

PeriodicTimer

相关推荐
哈__2 分钟前
MCP 协议落地实践:国产化数据库 AI 一站式 SQL 诊断调优方案
数据库·人工智能·sql
大不点wow5 分钟前
为什么 Spring 更推荐构造器注入,而不是字段 `@Autowired`
java·spring·intellij-idea
一招合理19 分钟前
Accordion:一种HBase内存压缩算法
大数据·数据库·hbase
用户2986985301427 分钟前
Java 创建 CSV 文件的三种实用方法:从零构建、数组写入与 Excel 转换
java·后端·excel
明亮 Blog28 分钟前
Wi-Fi 6/7的物联网“芯”机遇:高带宽低延时如何重塑智慧工厂的AGV物流革命
服务器·网络·数据库
Z59981784132 分钟前
c#软件开发学习笔记--事务、索引
笔记·学习·c#
Cry丶34 分钟前
一次 FTP 上传引发 504 Gateway Timeout 的排查复盘
java·nginx·ftp·openresty·被动模式·504 gateway·生产问题排查
geovindu39 分钟前
CSharp: Composite Pattern
开发语言·后端·c#·组合模式·结构型模式
LeoTao39 分钟前
C#专题-C# 异常处理
c#
码农进化录40 分钟前
Java 程序员的 AI 进化论 | AI 辅助 CI/CD,让 AI 帮你做代码审查和部署决策
java·spring boot·openai