C#基础-多线程

C# 多线程

闲得蛋疼,记录一下线程的基础概念和常规用法

基础概念

**进程:**应用程序(typerpa、word、IDEA)运行的时候进入到内存,程序在内存中占用的内存空间(进程)。

  • 是程序(任务)的一个执行过程,是动态的。
  • 持有资源(共享内存,共享文件)和线程。

线程:进程中负责程序运行的执行单元,也叫执行路径。

  • 可以将进程看作一个班级,线程看作班级里的学生,学生通过使用共享资源(教师、桌椅、黑板)进行学习。
  • 多线程技术解决了多部分代码同时执行的需求,能够更好的利用cpu的资源。

线程任务:线程执行的代码内容。

并行:多个cpu实例或者多台机器同时执行处理逻辑,实现真正的同时。

  • 如果电脑的CPU是多核处理器(4核),那就可以做到同时处理4个线程,从真正意义上做到多线程并行。线程的最大并行数量上限是CPU核心的数量,但是,往往电脑运行的线程的数量远大于CPU核心的数量,所以 还是需要CPU时间片的切换。

并发:通过cpu的调度算法,快速的切换线程执行线程任务,并不是真正意义上的同时。

线程安全:同一段代码在多线程使用下,不会因为线程的调度顺序而影响任何结果。在出现共享资源的情况下容易出现线程安全问题。确保线程的安全要优先于性能。

争用条件:当多个线程共享访问同一数据时,每个线程都尝试操作该数据,从而导致数据被破坏,这种现象称为争用条件。


注意,尽管多线程的使用可以提升程序的运行效率,充分发挥CPU的作用,但多线程并不是所有时候都可以随便使用的,多线程在时间和空间上都是有开销的,当超过一定限度之后,多线程开得越多,性能反而会下降。因此使用时应该根据实际需求,谨慎使用。

空间上的开销

  • Thread内核数据结构:线程的ID和线程环境都在CPU寄存器中占用空间。例如在CPU进行时间片切换的时候,没有处理完的数据会存储在CPU的相关空间中。
  • 用户堆栈模式:用户程序中的局部变量和参数传递所使用的堆栈。常见的异常:StackOverFlowException(堆栈溢出)就是内存占存过多,导致溢出异常。

时间上的开销

  • 资源使用通知的开销:因为一个程序会调用很多的托管和非托管的dll或exe资源,使用前需要先进行通知确认,这都是需要时间的。
  • 时间片切换开销。

Thread

一、线程的简单使用

在C#中启动多线程需要实例化一个 Thread对象,然后在构造方法中传入委托对象,为了方便一般会直接传入lambda表达式, 这个lambda表达式中执行的代码片段就是新的线程中所需要执行的任务。

最后,调用Thread对象Start()方法来启动新线程。

static void Main(string[] args)
{
    var thread = new Thread(() =>
    {
        Print();
    });
    thread.Start();
    Console.WriteLine($"主线程ID:{Thread.CurrentThread.ManagedThreadId}");
    Console.WriteLine("主线程结束!");
}

private static void Print()
{
    for (var i = 0; i< 100; i++)
    {
        Console.WriteLine($"子线程ID:{Thread.CurrentThread.ManagedThreadId}------{i}");
        Thread.Sleep(1000);
    }
}

另外提一下,因为线程的构造函数Thread()中所需要传入的参数是一个委托对象,所以如果任务函数不需要传入任何参数,可以直接将任务函数的名称作为参数传给构造函数。

var thread = new Thread(Print);

此外,如果需要提前终结线程,可以使用Thread对象的Abort()方法。

Abort():在调用此方法的线程上引发 System.Threading.ThreadAbortException,通过处理异常,去决定程序的终止与否(例如直接不理会异常导致整个程序终止),并不能直接终止线程。

  • 该方法已经过时弃用,不建议使用。

二、前台线程&后台线程

从上面的例子中可以看到,当程序运行起来后,主线程已经运行结束了,但是程序却没有停止,子线程依然在运行,依然每隔1秒输出一次。

这种情况下,持续运行的子线程就被称作前台线程(Foreground Thread)。

一般来说,在前台线程中执行的任务都比较重要,所以,只有等待所有的前台线程结束以后程序才能关闭。

相对而言,后台线程执行的任务就不是那么重要了,只要主线程和所有的前台线程执行结束,就算后台线程的任务还没完成,也会强行打断直接退出。

在 .Net中,除了主线程以外,任何一个线程都可以在任何时候在前台和后台之间随意切换。代码也非常简单,只需要将它的IsBackground属性设置为true,那么这个线程就可以被切换为后台线程了。

var thread = new Thread(() =>
{
    Print();
});
thread.IsBackground = true;
thread.Start();

特点:

后台线程不会影响进程的终止,属于某个进程的所有前台线程都终止后,该进程就会被终止,所有剩余的后台线程都会被强制停止。

可以在任何时候(线程开始前或开始后都可以)将前台线程修改为后台线程,方式是设置IsBackground属性。

不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止。

托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程。

三、线程池

线程是非常消耗资源的,如果我们每次需要子线程来执行任务,就去创建一个新的线程,那当我们执行1千次1万次甚至是100万次的时候,那么估计电脑硬件也受不了这么大的资源消耗。

对于这种情况我们就需要使用线程池来管理线程了。只要是通过线程池创建的线程,那么它的所有的生命周期都会托管在线程池内。所有线程池中的线程都是后台线程,线程池会检查程序当前的运行状态,当程序启动时,线程池内的线程也会开始工作;而当Main方法结束以后,线程池会自动关闭,同时中断所有正在运行的线程。

线程池的使用方式很简单,直接调用线程池ThreadPool类中的静态方法QueueUserWorkItem()来创建线程池内的线程就可以了。

static bool QueueUserWorkItem(WaitCallback callBack):参数为一个带一个object类型参数的委托,最后返回bool值成功则返回true

static void
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值