.NET/C#⾯试题汇总系列:多线程

news/2025/1/15 15:04:23/

1.根据线程安全的相关知识,分析以下代码,当调⽤test⽅法时i>10时是否会引起死锁?并简 要说明理由。

public void test(int i)
{lock(this){if (i>10){i--;test(i);}}
}

不会发⽣死锁,(但有⼀点int是按值传递的,所以每次改变的都只是⼀个副本,因此不会出现死锁。但如 果把int换做⼀个object,那么死锁会发⽣)

lock 关键字:

用于确保当多个线程同时访问共享资源时,不会发生数据竞争(race condition)或数据不一致的问题。

lock 语句通过获取给定对象的互斥锁来同步代码块,以确保一次只有一个线程可以执行该代码块。

  • 示例 : 线程安全的集合操作
public class ThreadSafeList<T>
{private readonly List<T> _list = new List<T>();private readonly object _lockObject = new object();public void Add(T item){lock (_lockObject){_list.Add(item);}}public void Remove(T item){lock (_lockObject){_list.Remove(item);}}public List<T> GetAll(){lock (_lockObject){return new List<T>(_list);}}
}

在这个例子中,我们创建了一个线程安全的列表类 ThreadSafeList<T>,其中 Add 和 Remove 方法使用 lock 来确保对列表的修改是线程安全的。GetAll 方法也使用 lock 来确保在返回列表的副本时,列表的状态是一致的。

2.描述线程与进程的区别?

  1. 定义与区别‌:

    • 进程‌(Process)是系统进行资源分配和调度的一个独立单元,是操作系统结构的基础。它是应用程序的一次动态执行过程,是程序代码及其数据在运行时的一个实例。它拥有独立的内存空间,包含一组系统资源(如代码、数据和打开的文件等)。
    • 线程‌(Thread)是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的独立运行的单位。一个进程可以拥有多个线程,这些线程共享该进程的所有资源(如内存和打开的文件),但每个线程都拥有自己独立的执行栈和程序计数器。
  2. 资源与开销‌:

    • 进程拥有独立的内存空间和系统资源,因此进程间的通信相对复杂,开销也较大。
    • 线程共享进程的资源,因此线程间的通信相对简单,开销也较小。
  3. 独立性‌:

    • 进程拥有更高的独立性,一个进程的崩溃通常不会影响到其他进程。
    • 线程则不然,一个线程的崩溃可能会导致整个进程的崩溃(取决于线程的异常处理机制)。

3.Windows单个进程所能访问的最⼤内存量是多少?它与系统的最⼤虚拟内存⼀样吗?这对 于系统设计有什么影响?

这个需要针对硬件平台,公式为单个进程能访问的最⼤内存量=2的处理器位数次⽅/2,⽐如通常情况下, 32位处理器下,单个进程所能访问的最⼤内存量为:232 /2 = 2G 。

单个进程能访问的最⼤内存量是最⼤ 虚拟内存的1/2,因为要分配给操作系统⼀半虚拟内存。

4.using() 语法有⽤吗?什么是IDisposable?

有⽤

实现了IDisposiable的类在using中创建,using结束后会⾃定调⽤该对象的Dispose⽅法,释放资 源。

  • using() 语法的用途

using 语句在C#中非常有用,特别是在处理实现了IDisposable接口的对象时。它确保了在代码块执行完毕后,会自动调用对象的Dispose方法,以释放非托管资源(如文件句柄、数据库连接等)。这不仅简化了资源管理,还避免了资源泄露的风险。

  • 什么是IDisposable

IDisposable是.NET中定义的一个接口,它包含一个Dispose方法。当一个类实现了IDisposable接口时,就意味着这个类拥有非托管资源,需要在不再需要时显式释放。Dispose方法通常用于释放这些非托管资源,并可能还包含对托管资源的清理逻辑(如关闭文件流、数据库连接等)。

通过使用using语句,我们可以确保即使发生异常,Dispose方法也会被调用,从而安全地释放资源。

例如:

using (var fileStream = new FileStream("example.txt", FileMode.OpenOrCreate))
{// 使用 fileStream 进行操作
}
// fileStream 的 Dispose 方法会在此处自动调用

FileStream类实现了IDisposable接口,因此我们可以使用using语句来确保fileStream在使用完毕后会被正确关闭和释放资源。

5.前台线程和后台线程有什么区别?

  • 前台线程(Foreground Thread)‌:在.NET中,默认创建的线程都是前台线程。只要应用程序中有任何前台线程在运行,应用程序就会保持运行状态。只有当所有的前台线程都终止时,应用程序才会结束。

  • 后台线程(Background Thread)‌:后台线程不会影响应用程序的终止。一旦所有的前台线程都终止,无论后台线程是否还在运行,应用程序都会自动结束。后台线程通常用于处理那些非关键的、可以异步执行的任务比如数据加载文件操作等。

6.什么是互斥?

当多个线程访问同⼀个全局变量,或者同⼀个资源(⽐如打印机)的时候,需要进⾏线程间的互斥操作来保证 访问的安全性。

7.如何查看和设置线程池的上下限?

  • 线程池的线程数是有限制的,通常情况下,我们⽆需修改默认的配置。但在⼀些场合,我们可能需要了解 线程池的上下限和剩余的线程数。线程池作为⼀个缓冲池,有着其上下限。在通常情况下,当线程池中的 线程数⼩于线程池设置的下限时,线程池会设法创建新的线程,⽽当线程池中的线程数⼤于线程池设置的 上限时,线程池将销毁多余的线程。
  • PS:在.NET Framework 4.0中,每个CPU默认的⼯作者线程数量最⼤值为250个,最⼩值为2个。⽽IO 线程的默认最⼤值为1000个,最⼩值为2个。

在.NET中,通过 ThreadPool 类型提供的5个静态⽅法可以获取和设置线程池的上限和下限,同时它还额 外地提供了⼀个⽅法来让程序员获知当前可⽤的线程数量,

下⾯是这五个⽅法的签名:

① static void GetMaxThreads(out int workerThreads, out int completionPortThreads)

② static void GetMinThreads(out int workerThreads, out int completionPortThreads)

③ static bool SetMaxThreads(int workerThreads, int completionPortThreads)

④ static bool SetMinThreads(int workerThreads, int completionPortThreads)

⑤ static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)

8. Task状态机的实现和⼯作机制是什么?

在.NET中,它会⾃动编译为:

  • 1. 将所有引⽤的局部变量做成 闭包,放到⼀个隐藏的状态机的类中;
  • 2. 将所有的await展开成⼀个状态号,有⼏个await就有⼏个状态 号;
  • 3. 每次执⾏完⼀个状态,都重复回调状态机的MoveNext⽅法,同时指定下⼀个状态号;
  • 4. MoveNext⽅法还需处理线程和异常等问题。

9.await的作⽤和原理,并说明和GetResult()有什么区别?

  • 从状态机的⻆度出发,await的本质是调⽤Task.GetAwaiter()的UnsafeOnCompleted(Action)回调,并 指定下⼀个状态号。
  • 从多线程的⻆度出发,如果await的Task需要在新的线程上执⾏,该状态机的MoveNext()⽅法会⽴即返 回,此时,主线程被释放出来了,然后在UnsafeOnCompleted回调的action指定的线程上下⽂中继续 MoveNext()和下⼀个状态的代码。
  • ⽽相⽐之下,GetResult()就是在当前线程上⽴即等待Task的完成,在Task完成前,当前线程不会释放。 注意:Task也可能不⼀定在新的线程上执⾏,此时⽤GetResult()或者await就只有会不会创建状态机的区 别了。

10.Task和Thread有区别吗?

  1. 抽象层次‌:Task 提供了比 Thread 更高的抽象层次。它代表了异步操作,可以很容易地与C#的异步编程模式(async/await)集成。Thread 则更底层,直接表示操作系统线程。

  2. 资源管理‌:Task 池管理了任务的执行,可以更有效地重用线程,减少线程创建和销毁的开销。相比之下,直接使用 Thread 需要程序员手动管理线程的创建、执行和销毁。

  3. 异步编程支持‌:Task 天然支持C#的异步编程模式,使得编写异步代码更加简单和直观。而 Thread 需要通过额外的机制(如回调函数、轮询等)来实现类似的功能。

11.多线程有什么⽤?

  1. 提高CPU利用率:多线程使得在等待某些任务完成时,如I/O操作或用户输入,CPU可以转而执行其他线程的任务,从而大大提高了CPU的利用率。
  2. 提升系统性能:通过并行处理多个任务,多线程可以显著提高系统的处理能力和性能,尤其是在多核处理器系统中更为明显。
  3. 改善用户体验:在图形用户界面(GUI)应用中,多线程可以实现界面的响应式设计,即使后台正在进行耗时的操作,用户界面也能保持活跃,提升用户体验。
  4. 简化资源共享:同一进程下的所有线程共享进程的资源,如内存空间、文件句柄等,这使得不同任务之间的协调操作与数据交互更加简单高效。
  5. 实现复杂任务:对于需要同时处理多个独立但相关任务的场景,多线程提供了一个有效的解决方案。例如,在服务器端并发处理用户请求时,每个请求可以在一个独立的线程中处理,互不干扰,提高了服务的响应速度和效率。
  6. 优化资源分配:在多线程编程中,可以通过线程同步机制来控制对共享资源的访问,防止数据竞争和死锁的发生,确保系统的稳定性和可靠性。
  7. 增强程序灵活性:多线程技术使得开发者能够更灵活地安排程序的执行流程,通过创建和管理不同的线程,可以有效地应对各种复杂的程序需求。
  8. 支持实时系统:在需要快速响应外部事件的应用中,如实时数据处理或游戏开发,多线程技术可以提供必要的并发执行能力,以满足严格的时间要求。

12. 两个线程交替打印0~100的奇偶数

这道题就是说有两个线程,⼀个名为偶数线程,⼀个名为奇数线程,偶数线程只打印偶数,奇数线程只打 印奇数,两个线程按顺序交替打印。

static AutoResetEvent oddReady = new AutoResetEvent(false);
static AutoResetEvent evenReady = new AutoResetEvent(true);//偶数线程先开始
static void Main(string[] args)
{Thread oddThread = new Thread(PrintOddNumbers);Thread evenThread = new Thread(PrintEvenNumbers);oddThread.Start();evenThread.Start();oddThread.Join();evenThread.Join();Console.WriteLine("打印完成。");
}
static void PrintOddNumbers()
{for (int i = 1; i <= 10; i+=2) { evenReady.WaitOne();// 等待偶数线程完成Console.WriteLine(i);oddReady.Set(); // 通知奇数线程可以运行}
}
static void PrintEvenNumbers()
{for (int i = 0; i <= 100; i += 2){oddReady.WaitOne(); // 等待奇数线程完成Console.WriteLine(i);evenReady.Set(); // 通知偶数线程可以运行}
}

AutoResetEvent 是 C# 中的一个类,用于实现线程同步。它属于 System.Threading 命名空间。

AutoResetEvent 有两种状态:有信号(true)和无信号(false)。

当一个线程调用 WaitOne() 方法时,如果事件处于无信号状态,那么该线程将被阻塞,直到另一个线程调用 Set() 方法将事件设置为有信号状态

一旦事件被设置为有信号状态,WaitOne() 方法将返回,并且事件将自动重置为无信号状态。

13.为什么GUI不⽀持跨线程调⽤?有什么解决⽅法?

因为GUI应⽤程序引⼊了⼀个特殊的线程处理模型,为了保证UI控件的线程安全,这个线程处理模型不允许 其他⼦线程跨线程访问UI元素。

解决⽅法⽐较多的

  • 利⽤UI控件提供的⽅法,Winform是控件的Invoke⽅法,WPF中是控件的Dispatcher.Invoke⽅法;
  • 使⽤BackgroundWorker;
  • 使⽤GUI线程处理模型的同步上下⽂SynchronizationContext来提交UI更新操作


http://www.ppmy.cn/news/1526204.html

相关文章

秃姐学AI系列之:实战Kaggle比赛:狗的品种识别(ImageNet Dogs)

目录 前置准备 整理数据集 图片增广 读取数据集 微调预训练模型 训练函数 训练和验证模型 Kaggle提交结果 前置准备 常规导包 import os import torch import torchvision from torch import nn from d2l import torch as d2l 使用小规模数据样本 d2l.DATA_HUB[dog…

GIS在线监测SF6密度微水传感器免焊接格兰头航插插头

概述 GIS&#xff08;气体绝缘金属封闭开关设备&#xff09;中的SF6&#xff08;六氟化硫&#xff09;气体密度微水传感器航插技术是指在GIS设备中安装SF6气体密度和微水传感器&#xff0c;以实现对SF6气体状态的在线监测。这些传感器能够实时监测SF6气体的密度、微水含量以及其…

鸿蒙交互事件开发04——手势事件

1 概 述 手势事件是移动应用开发中最常见的事件之一&#xff0c;鸿蒙提供了一些方法来绑定手势事件。通过给各个组件绑定不同的手势事件&#xff0c;并设计事件的响应方式&#xff0c;当手势识别成功时&#xff0c;ArkUI框架将通过事件回调通知组件手势识别的结果。 …

AI创意引擎:优化Prompt提示词的高效提问技巧

AI内容创作的精髓&#xff1a;提示词&#xff08;Prompt&#xff09; 在AI领域中&#xff0c;提示词&#xff08;Prompt&#xff09;是与模型沟通的关键工具。提示词不仅决定了AI生成内容的方向和质量&#xff0c;还在优化模型输出、提升用户体验中扮演着至关重要的角色。因此…

【机器学习】迁移学习的实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 迁移学习的实践迁移学习的常见框架1. 特征提取器微调(Fine-tuning the Feature …

获取某宝拍立淘API接口:深度学习图像实现匹配和检索

1. 总体概述 拍立淘的核心技术在于图像识别与检索&#xff0c;融合了深度学习、计算机视觉、大数据处理等多个领域的先进技术1。通过构建大规模的商品图像数据库&#xff0c;并利用深度学习算法提取图像特征&#xff0c;实现高效的图像匹配与检索1。 2. 具体技术环节 &#…

Unity 第一人称游戏的武器被其他物体覆盖解决方案

在第一人称游戏的时候&#xff0c;会出现渲染过程中&#xff0c;主角的手持武器可能会被其他物体挡住。 解决方法 在主摄像机下再创建一个摄像机&#xff0c;负责渲染不同图层 Main Camera的参数&#xff1a;我们这个摄像机不渲染equipable层&#xff08;自定义武器为equipab…

从ANN到SNN的转换:实现、原理及两种归一化方法【MINIST、实战】

从ANN到SNN的转换&#xff1a;实现、原理及两种归一化方法 引言 随着神经形态计算的迅猛发展&#xff0c;脉冲神经网络&#xff08;Spiking Neural Networks, SNNs&#xff09;作为一种仿生神经计算模型&#xff0c;逐渐展现出其在低功耗和事件驱动计算领域的巨大潜力。不同于…