C#面试常考随笔16:多线程汇总,Task状态机,async和await关键字

1.描述进程与线程的区别

定义上:

  • 进程:进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。简单来说,当你运行一个程序,操作系统就会为这个程序创建一个进程,它包含了程序运行所需的各种资源,如内存空间、文件句柄等。例如,当你打开一个记事本程序,操作系统就会创建一个记事本进程。
  • 线程:线程是进程中的一个执行单元,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程有自己独立的执行路径和栈空间。比如在一个视频编辑软件进程中,可能有一个线程负责播放视频,另一个线程负责处理用户的操作输入。

 通讯上:

  • 进程:由于进程之间的资源相互隔离,需要使用特定的进程间通信(IPC)机制,如管道、消息队列、共享内存、套接字等。例如,在一个分布式系统中,不同的进程可能分布在不同的计算机上,它们可以通过网络套接字进行通信。
  • 线程:线程之间共享所属进程的资源,因此它们之间的通信相对简单。可以直接访问共享的变量和对象,通过共享内存进行数据交换。但需要注意的是,由于多个线程可以同时访问共享资源,可能会导致数据竞争和线程安全问题,需要使用同步机制(如锁、信号量等)来保证数据的一致性。例如,在一个多线程的数据库应用程序中,多个线程可能同时访问和修改数据库连接池中的连接对象,需要使用锁来保证同一时间只有一个线程可以访问连接对象。

并发性能:

  • 进程:进程之间的并发是由操作系统进行调度的,不同进程可以在不同的 CPU 核心上并行执行,从而充分利用多核处理器的性能。但由于进程的创建和销毁开销较大,以及进程间通信的复杂性,进程级并发的性能可能会受到一定的影响。例如,在一个多核服务器上,可以同时运行多个独立的数据库进程,每个进程负责处理一部分数据请求。
  • 线程:线程的并发性能相对较高,因为线程的创建和销毁开销较小,且线程之间的切换速度较快。多个线程可以在同一个进程内并发执行,充分利用 CPU 的时间片。但当线程数量过多时,会增加 CPU 的调度开销,可能会导致性能下降。例如,在一个图像处理程序中,可以创建多个线程来同时处理不同区域的图像数据,提高处理速度。

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

通过将 Thread.IsBackground 属性设置为 true,就可以将线程指定为后台线程。

简单来说

前台线程: 应⽤必须结束掉所有的前台线程才能结束程序,只要有⼀个前台线程没退出进程就不会⾃动退出。前台线程通常用于执行一些需要完整执行过程的任务,例如用户界面交互、数据处理等

后台线程:进程可以不考虑后台直接⾃动退出,进程⾃动退出后所有的后台线程也会⾃动销毁。后台线程常用于执行一些辅助性的任务,如日志记录、垃圾回收等

详细介绍: C#代码,前台线程和后台线程有什么区别?-优快云博客

 3.Task状态机工作原理

Task 是用于表示异步操作的核心类型,而 Task 状态机则是 async/await 关键字背后的实现机制。当使用 async 和 await 关键字编写异步代码时,编译器会将其转换为一个状态机,这个状态机负责管理异步操作的执行流程。

async Task MyAsyncMethod()
{
  Console.WriteLine("Before await");
  await SomeAsyncOperation(); // 这里会生成状态机代码
  Console.WriteLine("After await");
}

await SomeAsyncOperation() 的执行会生成状态机代码,将异步操作的执行过程以状态机的形式表示,使得在异步操作未完成之前,MyAsyncMethod 可以被暂时挂起而不阻塞线程。 

工作机制

异步方法启动: 当调用一个使用 async 修饰的异步方法时,该方法的执行将立即返回一个 Task 对象,表示异步操作的状态。

生成状态机: 在编译时,编译器会对包含 await 关键字的异步方法生成一个状态机。这个状态机是一个类,负责跟踪异步操作的状态和执行过程。

挂起和继续: 当 await 关键字遇到一个尚未完成的异步操作时,异步方法会被暂停,状态机会将控制权还给调用者。当异步操作完成时,状态机会通过回调机制通知异步方法继续执行。

非阻塞: 异步方法的执行过程中,不会阻塞线程。线程可以继续执行其他任务,提高了程序的并发性。

Task 完成: 当异步操作完成时,相关的 Task 对象的状态会从等待状态变为完成状态,可以通过 Task 的 Result 属性获取异步操作的结果。

运用场景

1.异步 I/O 操作

当进行文件读写、网络请求等 I/O 密集型操作时,使用 Task 状态机可以避免阻塞主线程,提高应用程序的响应性能。

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        try
        {
            string filePath = "test.txt";
            // 异步读取文件内容
            string content = await File.ReadAllTextAsync(filePath);
            Console.WriteLine(content);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}
2.并行计算

在需要同时执行多个计算任务的场景中,可以使用 Task 状态机来并行执行这些任务,提高计算效率。

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Task<int> task1 = Task.Run(() => CalculateSum(1, 100));
        Task<int> task2 = Task.Run(() => CalculateSum(101, 200));

        int result1 = await task1;
        int result2 = await task2;

        int total = result1 + result2;
        Console.WriteLine($"Total sum: {total}");
    }

    static int CalculateSum(int start, int end)
    {
        int sum = 0;
        for (int i = start; i <= end; i++)
        {
            sum += i;
        }
        return sum;
    }
}
3.延迟操作

可以使用 Task.Delay 方法来实现延迟操作,同时不阻塞主线程。

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Starting delay...");
        await Task.Delay(2000); // 延迟 2 秒
        Console.WriteLine("Delay finished.");
    }
}

Task 状态机与 async 和 await 关键字之间的使用

async 关键字
  • async 关键字用于修饰方法,表示该方法是一个异步方法。异步方法可以包含 await 关键字,并且返回类型通常是 Task 或 Task<T>
  • 当编译器遇到 async 方法时,会将其转换为一个状态机。
 await 关键字
  • await 关键字用于等待一个 Task 完成。当执行到 await 表达式时,状态机会暂停当前方法的执行,保存当前状态,并将控制权返回给调用者。
  • 当 Task 完成后,状态机会恢复执行,并继续执行 await 之后的代码。

示例:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Console.WriteLine("Before async method call");
        await DoAsyncWork();
        Console.WriteLine("After async method call");
    }

    static async Task DoAsyncWork()
    {
        Console.WriteLine("Starting async work");
        await Task.Delay(1000); // 模拟异步操作
        Console.WriteLine("Async work finished");
    }
}

await 的原理

生成状态机: 编译器在编译时会生成一个状态机,用于跟踪异步操作的状态和执行过程。

任务挂起: 当 await 遇到一个未完成的异步操作时,它会将当前的异步方法的执行挂起。

注册回调: await 会注册一个回调,当异步操作完成时,回调会通知状态机,继续执行异步方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dr.勿忘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值