异步执行:
1、通过单线程实现并发操作。任务在等待I/O(网络、文件)时,不会阻塞其他任务的执行
2、更节省系统资源,不需要常见多个线程
3、适合I/O密集型任务,如网络请求,数据库查询等
4、示例
using System;
using System.Threading.Tasks;
class Program
{
static async Task Task1Async()
{
Console.WriteLine("任务1开始");
await Task.Delay(3000); // 模拟异步耗时操作
Console.WriteLine("任务1完成");
}
static async Task Task2Async()
{
Console.WriteLine("任务2开始");
await Task.Delay(3000); // 模拟异步耗时操作
Console.WriteLine("任务2完成");
}
static async Task Main(string[] args)
{
// 并发执行任务1和任务2
Task task1 = Task1Async();
Task task2 = Task2Async();
await Task.WhenAll(task1, task2); // 等待所有异步任务完成
Console.WriteLine("所有任务完成");
}
}
输出
任务1开始
任务2开始
任务1完成
任务2完成
所有任务完成
解释
·两个任务几乎同时开始,并且各自等待完成。总耗时大约为 3 秒,因为它们是并发执行的
多线程:
1、通过创建多个线程并行执行任务,每个任务都可以独立运行并充分利用多个 CPU 核心。
2、在某些场景下(如大量计算),多线程执行可以真正地实现任务并行处理。
3、适合CPU 密集型任务,例如复杂的计算和数据处理等。
4、示例
using System;
using System.Threading;
class Program
{
static void Task1()
{
Console.WriteLine("任务1开始");
Thread.Sleep(3000); // 模拟耗时操作
Console.WriteLine("任务1完成");
}
static void Task2()
{
Console.WriteLine("任务2开始");
Thread.Sleep(3000); // 模拟耗时操作
Console.WriteLine("任务2完成");
}
static void Main()
{
Thread thread1 = new Thread(Task1);
Thread thread2 = new Thread(Task2);
// 启动线程
thread1.Start();
thread2.Start();
// 等待线程结束
thread1.Join();
thread2.Join();
Console.WriteLine("所有任务完成");
}
}
输出
任务1开始
任务2开始
任务1完成
任务2完成
所有任务完成
解释:任务1和任务2在各自的线程上并行运行。总耗时大约为 3 秒。
练习:
区分Task.Delay(3000).Wait()
、await Task.Delay(3000)
和 Thread.Sleep(3000)
1、Thread.Sleep(3000)
同步阻塞,程序执行被阻塞,等待3秒后继续,程序“卡住”
2、Task.Delay(3000)
异步阻塞,程序执行继续,完全没影响
3、await Task.Delay(3000) 异步阻塞,程序执行被暂停,等待3秒后继续,但是程序不是“卡住”,当程序遇到await时,
当前线程可以去执行其他任务,线程释放控制权,而不是被“卡住”在某个等待操作上。待异步操作完成时,程序会从线程池中选择一个空闲线程来继续执行。
表现:暂停期间,线程空闲出来可以处理其他任务,等异步任务完成后,程序会继续执行挂起的部分(回调)
4、Task.Delay(3000).Wait() 调用wait办法,将异步阻塞变为同步阻塞!
区别概念:阻塞和释放控制权
线程同步:
概念:多线程环境下,通过协调线程对共享资源的访问,保证数据一致性和避免数据竞争的过程
目的:防止并发操作
实现:线程之间采用互斥或者信号量等方式进行同步
互斥锁:互斥锁是一种用来防止多个线程同时访问同一个资源的同步机制。当一个线程持有互斥锁时,其他线程必须等待,直到该线程释放互斥锁,才能访问该资源。
信号量:信号量用于控制对有限资源的访问。与互斥锁不同,信号量允许多个线程在同一时刻访问资源,但其数量是受限的。常见的应用场景是有一个资源池,多个线程可以访问其中的资源,但同时访问的线程数有限。
Task:
Task是一个调度单位,代表的是一个异步操作,而非线程。即Task可以在已存在的线程池(CLR预先创建)中执行,而不需要创建新线程。
并发和并行:
并发:在单核CPU上,多线程虽然在“同时”执行,但实际上他们是通过高速切换CPU的控制权(时间片轮转)来实现的,线程之间是轮流占用CPU。
并行:在多核CPU上,多个线程可以真正同时运行,每个线程都有不同的CPU核心。单线程数量超过核心数量时,仍然需要做并发操作(时间片轮转)来调度CPU