c# 面试必备线程基础知识点

线程的知识太多,知识点有深有浅,往深的研究会涉及操作系统、CPU、内存,往浅了说就是一些语法。没有一定的知识积累,很难把线程的知识写得全面,当然我也没有这个能力。所以想到一个点写一个点,尽量总结一些有用的知识点。线程是个大话题,这个系列可能会有好几遍关于线程的,先从基础的开始,热热身。

一些基础概念

线程(Thread)是操作系统能够进行运算调度的最小单位。它是进程中的实际运作单位,一个进程中可以启动多个线程,每个线程可以并行执行不同的任务。严格意义上来说,同一时间可以并行运行的线程数取决于 CPU 的核数。

根据线程运行模式,可以把线程分为前台线程、后台线程和守护(Daemon)线程:

  • 前台线程:主程序必须等待线程执行完毕后才可退出程序。C# 中的 Thread 默认为前台线程,也可以设置为后台线程。
  • 后台线程:主程序执行完毕立即跟随退出,不管线程是否执行完毕。C# 的 ThreadPool 管理的线程默认为后台线程。
  • 守护线程:守护线程拥有自动结束自己生命周期的特点,它通常被用来执行一些后台任务。

每次开启一个新的线程都要消耗一定的内存,即使线程什么也不做,也会至少消耗 1M 左右的内存。

多线程并行(Parallelism)和并发(Concurrency)的区别:

  • 并行:同一时刻有多条指令在多个处理器上同时执行,无论从宏观还是微观上都是同时发生的。
  • 并发:是指在同一时间段内,宏观上看多个指令看起来是同时执行,微观上看是多个指令进程在快速的切换执行,同一时刻可能只有一条指令被执行。

PS:以上概念来源 Google 的多个搜索结果,稍加整理。

Thread、ThreadPool 和 Task

对 C# 开发者来说,不可不理解清楚 Thread、ThreadPool 和 Task 这三个概念。这也是面试频率很高的话题,在 StackOverflow 可以找到有很多不错的回答,我总结整理了一下。

Thread

Thread 是一个实际的操作系统级别的线程(OS 线程),有自己的栈和内核资源。Thread 允许最高程度的控制,你可以 Abort、Suspend 或 Resume 一个线程,你还可以监听它的状态,设置它的堆栈大小和 Culture 等属性。Thread 的开销成本很高,你的每一个线程都会为它的堆栈消耗相对较多的内存,并且在线程之间的处理器上下文切换时会增加额外的 CPU 开销。

ThreadPool

ThreadPool(线程池)是一堆线程的包装器,由 CLR 维护。你对线程池中的线程没有任何控制权,你甚至无法知道线程池什么时候开始执行你提交的任务,你只能控制线程池的大小。简单来说,线程池调用线程的机制是,它首先调用已创建的空闲线程来执行你的任务,如果当前没有空闲线程,可能会创建新线程,也可能会等待。

使用 ThreadPool 可以避免创建太多线程的开销。但是,如果你向 ThreadPool 提交了太多长时间运行的任务,它可能会被填满,这时你提交的后面的任务可能最终会等待前面的长时间运行的任务执行完成。此外,线程池没有提供任何方法来检测一个工作任务何时完成(不像 Thread.Join()),也没有方法来获取结果。因此,ThreadPool 最好用于调用者不需要结果的短时操作。

Task

Task 是 TPL(Task Parallel Library)提供一个类,它在 Thread 和 TheadPool 之间提供了两全其美的解决方案。和 ThreadPool 一样,Task 并不创建自己的OS 线程。相反,Task 是由 TaskScheduler 调度器执行的,默认的调度器只是在 ThreadPool 上运行。

与 ThreadPool 不同的是,Task 还允许你知道它完成的时间,并获取返回一个结果。你可以在现有的 Task 上调用 ContinueWith(),使它在任务完成后运行更多的代码(如果它已经完成,就会立即运行回调)。

你也可以通过调用 Wait() 来同步等待一个任务的完成(或者,通过获取它的 Result 属性)。与 Thread.Join() 一样,这将阻塞调用线程,直到任务完成。通常不建议同步等待任务执行完成,它使调用线程无法进行任何其他工作。如果当前线程要等待其它线程任务执行完成,建议使用 async/await 异步等待,这样当前线程可以空闲出来去处理其它任务,比如在 await Task.Delay() 时,并不占用线程资源。

由于任务仍然在 ThreadPool 上运行,因此不应该将其用于长时任务的执行,因为它们会填满线程池并阻塞新的工作任务。相反,Task 提供了一个 LongRunning 选项,它将告诉 TaskScheduler 启用一个新的线程,而不是在 ThreadPool 上运行。

所有较新的上层多线程 API,包括 Parallel.ForEach()、PLINQ、async/await 等,都是建立在 Task 上的。

Thread 和 Task 简单示例

下面通过一个简单示例演示 Thread 和 Task 的使用,注意他们是如何创建、传参、执行和等待执行完成的。

static void Main(string[] args)
{
  // 创建两个新的 Thread
  var thread1 = new Thread(new ThreadStart(() => PerformAction("Thread", 1)));
  var thread2 = new Thread(new ThreadStart(() => PerformAction("Thread", 2)));

  // 开始执行线程任务
  thread1.Start();
  thread2.Start();

  // 等待两个线程执行完成
  thread1.Join();
  thread1.Join();

  Console.WriteLine("Theads done!");

  Console.WriteLine("===我是分隔线===");

  // 创建两个新的 Task
  var task1 = Task.Run(() => PerformAction("Task", 1));
  var task2 = Task.Run(() => PerformAction("Task", 2));

  // 执行并等待两个 Task 执行完成
  Task.WaitAll(new[] { task1, task2 });

  Console.WriteLine("Tasks done!");

  Console.ReadKey();
}

static void PerformAction(string threadOrTask, int id)
{
  var rnd = new Random(id);
  for (int i = 0; i < 5; i++)
  {
    Console.WriteLine($"{threadOrTask}: {id}: {i}", id, i);
    Thread.Sleep(rnd.Next(0, 1000));
  }
}

运行效果:

注意到,相比之下 Task 比 Thread 好用得多,加上前文 Task 和 Thread 的对比,对我们编码的指导意义是:大多数情况我们应该使用 Task,而不要直接使用 Thread,除非你明确知道你需要一个独立的线程来执行一个长耗时的任务。

小结

本篇内容很基础,整理了 C# 线程编程有关的重要概念,简单演示了 Thread 和 Task 的使用。Thread 和 Task 是高频面试话题,尤其是 Thread 和 Task 的区别,Thread 更底层,Task 更抽象,回答好这类面试题的关键点在 ThreadPool。

作者:精致码农

出处:http://cnblogs.com/willick

以上就是c# 面试必备线程基础知识点的详细内容,更多关于c# 线程基础的资料请关注我们其它相关文章!

(0)

相关推荐

  • C#线程委托实现原理及方法解析

    很多时候写windows程序都需要结合多线程,在C#中用如下得代码来创建并启动一个新的线程. Thread thread = new Thread(new ThreadStart(ThreadProc));//实例化一个线程 thread.IsBackground = true;//将线程改为后台线程 thread.Start();//开启线程 但是很多时候,在新的线程中,我们需要与UI(Windows窗体设计器用户界面)进行交互,在C#中不允许直接这样做.可以参考MSDN中的描述. "Wind

  • 浅析c# 线程同步

    同步是一种只允许一个线程在特定时间访问某些资源的技术.没有其他线程可以中断,直到所分配的线程或当前访问线程访问数据完成其任务. 在多线程程序中,允许线程访问任何资源所需的执行时间.线程共享资源并异步执行. 访问共享资源(数据)是有时可能会暂停系统的关键任务.所以可以通过线程同步来处理它. 主要场景如:存款,取款等交易业务处理. 线程同步的优点 一致性维护 无线程干扰 C#锁定 使用 C# lock关键字同步执行程序.它用于为当前线程锁定,执行任务,然后释放锁定.它确保其他线程在执行完成之前不会中

  • C#开启线程的四种示例

    1.异步委托开启线程 public class Program { public static void Main(string[] args) { Action<int, int> a = add; a.BeginInvoke(3, 4, null, null); Console.WriteLine("执行线程"); Console.ReadKey(); } static void add(int a, int b) { Console.WriteLine(a + b);

  • 深入了解c#多线程编程

    一.使用线程的理由 1.可以使用线程将代码同其他代码隔离,提高应用程序的可靠性. 2.可以使用线程来简化编码. 3.可以使用线程来实现并发执行. 二.基本知识 1.进程与线程:进程作为操作系统执行程序的基本单位,拥有应用程序的资源,进程包含线程,进程的资源被线程共享,线程不拥有资源. 2.前台线程和后台线程:通过Thread类新建线程默认为前台线程.当所有前台线程关闭时,所有的后台线程也会被直接终止,不会抛出异常. 3.挂起(Suspend)和唤醒(Resume):由于线程的执行顺序和程序的执行

  • C#多线程基础知识汇总

    最近自己写了个小爬虫,里面用到了多线程技术,忽然发现对此技术竟然有些陌生了,于是乎开始疯狂的去问度娘,在此记录下来,以便自己和各位小伙伴们学习. 一.什么是线程 一个应用程序就相当于一个进程,进程拥有应用程序的所有资源进程包括线程,进程的资源被线程共享,但不拥有线程.我们可以打开电脑中的任务管理器,运行的.exe都是一个进程,里面的分支是线程. 二.多线程 多线程其实就是进程中一段并行运行的代码 1. 创建并启动线程 static void Main() { //获取线程Id var threa

  • 实例代码讲解c# 线程(上)

    简介 过去普通计算机只有计算单元,却不能同时执行多个任务.然而操作系统却已经可以同时运行多个应用程序,即实现了多任务的概念.为了防止一个应用程序控制CPU而导致其他应用程序和操作系统本身永远被挂起这一可能情况,操作系统不得不使用某种方式 将物理计算单元分割为一些虚拟的进程,并给予每个程序一定量的计算能力.此外,操作系统必须始终能够优先访问CPU,并能调整不同程序访问CPU的优先级.线程正是这一概念的实现.可以认为线程是一个虚拟进程,用于独立运行一个特定的程序. (请记住线程会消耗大量的操作系统资

  • c# 进程之间的线程同步

    Mutex类.Event类.SemaphoreSlim类和ReaderWriterLockSlim类等提供了多个进程之间的线程同步.  1.WaitHandle 基类 WaitHandle抽象类,用于等待一个信号的设置.可以根据其派生类的不同,等待不同的信号.异步委托的BeginInvoke()方法返回一个实现了IAsycResult接口的对象.使用IAsycResult接口可以用AsycWaitHandle属性访问WaitHandle基类.在调用WaitOne()方法时,线程会等待接收一个和等

  • C#中多线程ManualResetEvent 与 AutoResetEvent 区别

    在多线程开发中,时常用到ManualResetEvent 与AutoResetEvent . 它们如同道路交通中的信号灯.两者之间有什么区别呢? 共同点: 均继承EventWaitHandle 接口,因此,均具有以下功能: Reset() //红灯 Set() //绿灯 WaitOne() // 等待信号 不同点: AutoResetEvent 收到 Set 后 , 一次只能执行一个线程,其它线程继续 WaitOne . ManualResetEvent 收到 Set 后,所有处理 WaitOn

  • C# 多线程编程技术基础知识入门

    什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程? 线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数. 什么是多线程? 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务. 多线程是指程序中包含多个执行流,即在一个程序中

  • c# 面试必备线程基础知识点

    线程的知识太多,知识点有深有浅,往深的研究会涉及操作系统.CPU.内存,往浅了说就是一些语法.没有一定的知识积累,很难把线程的知识写得全面,当然我也没有这个能力.所以想到一个点写一个点,尽量总结一些有用的知识点.线程是个大话题,这个系列可能会有好几遍关于线程的,先从基础的开始,热热身. 一些基础概念 线程(Thread)是操作系统能够进行运算调度的最小单位.它是进程中的实际运作单位,一个进程中可以启动多个线程,每个线程可以并行执行不同的任务.严格意义上来说,同一时间可以并行运行的线程数取决于 C

  • Python工程师面试必备25条知识点

    Python工程师面试必备25条Python知识点: 1.到底什么是Python?你可以在回答中与其他技术进行对比 下面是一些关键点: Python是一种解释型语言.这就是说,与C语言和C的衍生语言不同,Python代码在运行之前不需要编译.其他解释型语言还包括PHP和Ruby. Python是动态类型语言,指的是你在声明变量时,不需要说明变量的类型.你可以直接编写类似x=111和x="I'm a string"这样的代码,程序不会报错. Python非常适合面向对象的编程(OOP),

  • Java面试必备之ArrayList陷阱解析

    目录 问题分析 疑惑满满 拨云见日 回顾整个过程 如何正确的删除 总结 问题分析 疑惑满满 小枫听到这个面试题的时候,心想这是什么水面试官,怎么问这么简单的题目,心想一个for循环加上equal判断再删除不就完事了吗?但是转念一想,不对,这里面肯定有陷阱,不然不会问这么看似简单的问题.小枫突然想起来之前写代码的时候好像遇到过这个问题,也是在ArrayList中删除指定元素,但是直接for循环remove元素的时候还抛出了异常,面试官的陷阱估计在这里.小枫暗自窃喜,找到了面试官埋下的陷阱. 小枫回

  • Java面试必备之JMM高并发编程详解

    目录 一.什么是JMM 二.JMM定义了什么 原子性 可见性 有序性 三.八种内存交互操作 四.volatile关键字 可见性 volatile一定能保证线程安全吗 禁止指令重排序 volatile禁止指令重排序的原理 五.总结 一.什么是JMM JMM就是Java内存模型(java memory model).因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题.所以java内存模型(JMM)屏蔽掉各种硬件和操作系统的内存访问差

  • Java并发中线程封闭知识点详解

    在这篇文章中,我们将探讨线程封闭是什么意思,以及我们如何实现它. 所以,让我们直接开始吧. 线程封闭基础知识点 实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢? 就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程安全的也不会出现任何安全问题.实现线程封闭有哪些方法呢? 1:ad-hoc线程封闭 这是完全靠实现者控制的线程封闭,他的线程封闭完全靠实现者实现.也是最糟糕的一种线程封闭.所以我们直接把他忽略掉

  • MySQL必备的常见知识点汇总整理

    本文实例总结了MySQL必备的常见知识点.分享给大家供大家参考,具体如下: 最近在整理 sql 的时候发现一份优秀的笔记,是原作者学习 sql 所做的笔记,分享这份总结给大家,对大家对 sql 的可以来一次全方位的检漏和排查,感谢原作者 hjzCy 的付出,原文链接放在文章最下方,如果出现错误,希望大家共同指出! 登录和退出 MySQL 服务器 # 登录MySQL $ mysql -u root -p12345612 # 退出MySQL数据库服务器 exit; 基本语法 -- 显示所有数据库 s

  • Java面试必备之AQS阻塞队列和条件队列

    一.AQS入队规则 我们仔细分析一下AQS是如何维护阻塞队列的,在独占方式获取资源的时候,是怎么将竞争锁失败的线程丢到阻塞队列中的呢? 我们看看acquire方法,这里首先会调用子类实现的tryAcquire方法尝试修改state,修改失败的话,说明线程竞争锁失败,于是会走到后面的这个条件: 这个addWaiter方法就是将当前线程封装成一个Node.EXCLUSIVE类型的节点,然后丢到阻塞队列中: 第一次还没有阻塞队列的时候,会到enq方法里面,我们仔细看看enq方法 enq()方法中,我们

  • java局部变量表的基础知识点及实例

    说明 1.局部变量表也叫局部变量数组或本地变量表.定义为一个数组,主要用于存储方法参数和定义方法中的局部变量.这些数据类型包括各种基本数据类型.对象参考和returnAddress类型. 2.因为局部变量表是建立在线程栈上的线程私有数据,所以没有数据安全问题. 实例 private static int count=0; public static void recursion(int a,int b,int c){ long l1=12; short sl=1; byte b1=1; Stri

  • Mysql数据库面试必备之三大log介绍

    目录 一.redo log 重做日志(MySQL 存储引擎 InnoDB 的事务日志) 二.undo log 回滚日志(MySQL 存储引擎 InnoDB 的事务日志) 三.bin log 归档日志(数据库 Server 层二进制逻辑日志.和什么引擎无关) 快,开篇大伙先思考一个问题,MySQL 是怎么保证数据不丢失的呢? 其实要保证数据不丢失,说白了要具有下面两种能力: (1)能恢复到任何时间点的状态: (2)能保证 MySQL 在任何时间段突然宕机重启,已提交的数据不会丢失,未提交完整的数据

  • 面试必备之ajax原始请求

    目录 XMLHttpRequest 对象 简介 XMLHttpRequest 的实例属性 XMLHttpRequest.readyState XMLHttpRequest.onreadystatechange XMLHttpRequest.response XMLHttpRequest.responseType XMLHttpRequest.responseText XMLHttpRequest.responseXML XMLHttpRequest.responseURL XMLHttpRequ

随机推荐