C# 多线程 未完

发布于:2024-04-22 ⋅ 阅读:(37) ⋅ 点赞:(0)

C# 多线程

基本概念

什么是线程

线程是能独立运行的最小单位,也是程序能够并发执行的一段指令序列
线程是进程的一部分,一个进程可以包含多个线程,这些线程共享进程的资源
进程有入口线程,可以创建更多的线程


为什么要多线程

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

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

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

比如我主线程有一个任务读取变换的数,然后我额外开一个后台线程去发送我读取的数
不然我读取变化的数进行发送,发送十分耗时,这将导致我读取不完全


什么是线程池

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

一个线程的创建和销毁是十分耗时的,开销很大
当我们需要执行一个小任务时,直接找线程池要一个,做完再把线程还给他
避免频繁地创建和销毁线程,从而减少了线程创建和销毁的开销,提高了系统的性能和效率

异步编程默认使用线程池

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


什么是线程安全

线程安全

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

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.异步化 异步,非同步化
考虑一下自带方法
`Parallel`		For、ForEach、Invoke
`PLINQ`  		AsParallel、AsSequential、AsOrdered

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



线程Thread

线程的创建

创建Thread实例,并传入ThreadStart委托
还可以配置线程,如是否为后台线程
调用Thread.Start方法,还可以传参


线程的终止

调用Thread.Join方法,等待线程的结束
意味着谁Join,我就要等谁结束了再继续别的事

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

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

锁与信号量

轻量型

不要自己造轮子