1. 异步编程(Asynchronous Programming)
异步编程是通过非阻塞方式执行任务,通常适用于I/O 密集型任务,例如文件读写、网络请求、数据库访问等。这类操作不需要占用大量 CPU 资源,而是等待外部资源(如网络或硬盘)响应。
在 C# 中,异步编程使用 async
和 await
关键字来实现。异步方法会返回 Task
或 Task<T>
,调用 await
后,主线程可以继续执行其他代码,不会被阻塞。异步任务完成后,程序会回到 await
的位置继续执行。
异步代码示例
以下是一个使用异步编程的示例:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Starting download...");
string content = await DownloadContentAsync("https://example.com");
Console.WriteLine("Download completed!");
Console.WriteLine($"Content length: {content.Length}");
}
static async Task<string> DownloadContentAsync(string url)
{
using HttpClient client = new HttpClient();
// 异步地开始下载内容
string content = await client.GetStringAsync(url);
return content;
}
}
在这个例子中,DownloadContentAsync
是一个异步方法。调用 await DownloadContentAsync(...)
后,Main
方法不会被阻塞,它会释放 CPU,等到下载完成后再继续执行。这种非阻塞模式适合 I/O 密集型任务。
这里该怎么理解,为什么不会阻塞,举个实际业务的例子,就是微信支付的接口并不马上返回支付结果,这就是没有阻塞线程,线程继续做其他事情,支付结果通过回调接口通知;那么如果不是异步会是什么样的,那就是一直等待微信支付的接口响应,这个时候线程就被阻塞了。异步可以理解成回调,进一步说.Net程序向操作系统读取文件,访问网络,什么时候读到了文件,什么时候网络有了响应再回调给.Net程序,.Net程序再启用线程池的线程继续完成回调后的任务,这也是异步后线程id会改变的原因。
2. 多线程编程(Multithreading Programming)
多线程编程是通过在多个线程上并行执行任务,适用于CPU 密集型任务,如复杂计算、数据处理等。这类操作需要充分利用 CPU 的多核心能力来同时处理多个任务。
在 C# 中,多线程编程可以通过 Thread
、ThreadPool
、Task
等类实现。与异步不同,多线程会创建多个线程来执行不同的任务,这样多个任务可以并发运行。
多线程代码示例
以下是一个使用多线程的示例:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
Console.WriteLine("Starting CPU-intensive tasks...");
// 启动两个并行任务
Task task1 = Task.Run(() => PerformCpuIntensiveTask("Task 1"));
Task task2 = Task.Run(() => PerformCpuIntensiveTask("Task 2"));
// 等待任务完成
Task.WaitAll(task1, task2);
Console.WriteLine("All tasks completed.");
}
static void PerformCpuIntensiveTask(string taskName)
{
Console.WriteLine($"{taskName} started.");
for (int i = 0; i < 1000000000; i++)
{
// 模拟计算任务
double x = Math.Sqrt(i);
}
Console.WriteLine($"{taskName} completed.");
}
}
在这个例子中,Task.Run
会在 ThreadPool
中创建并启动新的线程来执行 PerformCpuIntensiveTask
,这样 Task 1
和 Task 2
就可以并行执行,提高了 CPU 使用效率。
其实无论是时阻塞的耗时还是复杂计算的耗时,这两个都是耗时,但是这两个又有很大区别,阻塞的耗时可以通过回调,释放当前线程来解决阻塞的问题,但是复杂计算的耗时核回调就没有关系了。举个具体点的例子,要计算一个天文级别的数字,一个人要10天,但是你发现这些数子是可以拆开来分给不同的人计算,最后把结果加起来就行了。这种情况下使用多线程处理计算任务,然后把结果加起来,减少了耗时,同时也提高了cpu的使用率。
总结
- 异步主要用于非阻塞 I/O 操作,让程序在等待 I/O 时释放资源,适合 I/O 密集型任务。适用于串行任务,不提升单次耗时,但是对整个程序来说可以提示吞吐量。
- 多线程用于让多个任务并行执行,充分利用 CPU 核心资源,适合 CPU 密集型任务。之前的单次任务可以拆分给不同任务执行的话可以提升耗时,通过消耗硬件资源实现并行或者并发提升效率。