代码改变世界

C#.NET面试题高级篇-多线程

2022-04-08 10:09  蛮荒古神  阅读(4174)  评论(0编辑  收藏  举报

1.说说常用的锁,lock是一种什么样的锁?

    常用的如如SemaphoreSlim、ManualResetEventSlim、Monitor、自旋锁SpinLock、读写锁ReadWriteLockSlim,lock是一个混合锁,其实质是Monitor

 

2.lock为什么要锁定一个参数(可否为值类型?)参数有什么要求?

    lock的锁对象要求为一个引用类型。她可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效。

    对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用。

 

3.多线程和异步的区别和联系?

    多线程是实现异步的主要方式之一,异步并不等同于多线程。实现异步的方式还有很多,比如利用硬件的特性、使用进程或线程等。

    在.NET中就有很多的异步编程支持,比如很多地方都有Begin、End 的方法,就是一种异步编程支持,她内部有些是利用多线程,有些是利用硬件的特性来实现的异步编程。

 

 

4.线程池的优点有哪些?又有哪些不足

    优点:减小线程创建和销毁的开销,可以复用线程;也从而减少了线程上下文切换的性能损失;在GC回收时,较少的线程更有利于GC的回收效率。

    缺点:线程池无法对一个线程有更多的精确的控制,如了解其运行状态等;不能设置线程的优先级;加入到线程池的任务(方法)不能有返回值;对于需要长期运行的任务就不适合线程池。

 

5.Mutex和lock有什么不同?一般用哪一种比较好?

    Mutex是一个基于内核模式的互斥锁,支持锁的递归调用,而Lock是一个混合锁,一般建议使用Lock更好,因为lock的性能更好。

 

6.Thread 类有哪些常用的属性和方法?

属性:

CurrentContext:获取线程正在其中执行的当前上下文。

CurrentCulture:获取或设置当前线程的区域性。

CurrentPrincipal:获取或设置线程的当前负责人(对基于角色的安全性而言)。

CurrentThread:获取当前正在运行的线程。

CurrentUICulture:获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。

IsBackground:获取或设置一个值,该值指示某个线程是否为后台线程。

Priority:获取或设置一个值,该值指示线程的调度优先级。

ThreadState:获取一个值,该值包含当前线程的状态。

方法:

public void Abort()
在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。调用此方法通常会终止线程。

public static void ResetAbort()

取消为当前线程请求的 Abort。

public void Start()
开始一个线程。

public static void Sleep( int millisecondsTimeout )

让线程暂停一段时间。

public static bool Yield()

导致调用线程执行准备好在当前处理器上运行的另一个线程。由操作系统选择要执行的线程。

 

7、聊聊任务Task与并行Parallel

任务Task与并行Parallel本质上内部都是使用的线程池,提供了更丰富的并行编程的方式。任务Task基于线程池,可支持返回值,支持比较强大的任务执行计划定制等功能,下面是一个简单的示例。Task提供了很多方法和属性,通过这些方法和属性能够对Task的执行进行控制,并且能够获得其状态信息。Task的创建和执行都是独立的,因此可以对关联操作的执行拥有完全的控制权。

//创建一个任务Task<int> t1 = new Task<int>(n =>{    System.Threading.Thread.Sleep(1000);    return (int)n;}, 1000);//定制一个延续任务计划t1.ContinueWith(task =>{    Console.WriteLine("end" + t1.Result);}, TaskContinuationOptions.AttachedToParent);t1.Start();//使用Task.Factory创建并启动一个任务var t2 = System.Threading.Tasks.Task.Factory.StartNew(() =>{    Console.WriteLine("t1:" + t1.Status);});Task.WaitAll();Console.WriteLine(t1.Result);

并行Parallel内部其实使用的是Task对象(TPL会在内部创建System.Threading.Tasks.Task的实例),所有并行任务完成后才会返回。少量短时间任务建议就不要使用并行Parallel了,并行Parallel本身也是有性能开销的,而且还要进行并行任务调度、创建调用方法的委托等等。

 

8、下面代码输出结果是什么?为什么?

int a = 0;
System.Threading.Tasks.Parallel.For(0, 100000, (i) =>
{
a++;
});
Console.Write(a);

输出结果不稳定,小于等于100000。因为多线程访问,没有使用锁机制,会导致有更新丢失。

 

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

  • 并行:同一时刻有多条指令在多个处理器上同时执行,无论从宏观还是微观上都是同时发生的。

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

     

10、C# Parallel.For和普通For的区别

Parallel类是.NET 4中新增的抽象线程类。Parallel.For()方法类似于C#的for循环语句,也是多次执行一个任务。但是使用Parallel.For()方法,可以并行运行。

对于Parallel.For、Parallel.Foreach的使用应该要特别小心,它们的优势是处理列表很长,且对列表内的元素进行很复杂的业务逻辑,且不会使用共享资源,只针对自身的业务逻辑处理,方才能提升效率。