关注墨瑾轩,带你探索编程的奥秘!
超萌技术攻略,轻松晋级编程高手
技术宝库已备好,就等你来挖掘
订阅墨瑾轩,智趣学习不孤单
即刻启航,编程之旅更有趣
目标:让任务“即发即弃”,像“快递员”一样独立执行!
核心思想:
“像给任务装上‘火箭助推器’,执行完就消失,主线程继续干大事!”
步骤1:定义阻塞任务(如发送邮件)
// 阻塞任务示例:发送邮件
public class EmailService {
public void SendEmail(string to, string subject, string body) {
// 模拟耗时操作(如发送邮件)
Thread.Sleep(3000); // 3秒延迟
Console.WriteLine("邮件已发送!");
}
}
步骤2:在业务逻辑中“即发即弃”执行
// 注册服务(同步执行会阻塞)
public class UserService {
private readonly EmailService _emailService;
public UserService(EmailService emailService) {
_emailService = emailService;
}
// 问题代码:同步调用(主线程卡住)
public void RegisterUser(string email) {
// ... 保存用户到数据库 ...
_emailService.SendEmail(email, "欢迎", "感谢注册!"); // 同步执行,主线程等3秒
}
}
// ✅ 修复后:异步“即发即弃”
public void RegisterUser(string email) {
// ... 保存用户到数据库 ...
_ = Task.Run(() => { // 甩手掌柜模式
try {
_emailService.SendEmail(email, "欢迎", "感谢注册!");
} catch (Exception ex) {
// 异常处理:记录日志或重试
Console.WriteLine($"发送失败:{ex.Message}");
}
});
}
效果对比:
目标:让任务“零开销启动”,像“闪电侠”一样飞速执行!
核心思想:
“像给任务换上‘超跑引擎’,避免不必要的内存分配,执行更快!”
步骤1:定义高性能异步任务
// 使用ValueTask替代Task,减少内存分配
public class FastService {
public async ValueTask DoFastWork() { // 使用ValueTask
// 轻量级异步操作
await Task.Delay(100); // 模拟IO操作
Console.WriteLine("超快速任务完成!");
}
}
步骤2:在业务逻辑中“即发即弃”
public class BusinessService {
private readonly FastService _fastService;
public BusinessService(FastService fastService) {
_fastService = fastService;
}
public void ProcessRequest() {
// 即发即弃:不等待,不阻塞
_ = _fastService.DoFastWork().AsTask(); // 强制转换为Task
// 立即返回响应
Console.WriteLine("主线程继续干活!");
}
}
性能对比:
方案 | 内存分配 | 启动时间 | 适用场景 |
---|---|---|---|
Task | 高(需创建Task对象) | 较慢 | 通用场景 |
ValueTask | 极低(无对象分配) | 极快 | 短任务/高频调用 |
目标:让线程池“超频运行”,像“加油站”一样持续供能!
核心思想:
“像给线程池装上‘无限弹药’,避免任务堆积导致的性能崩溃!”
步骤1:配置线程池(在Program.cs中)
// 自定义线程池配置
public class Program {
public static void Main(string[] args) {
// 扩大线程池容量
ThreadPool.SetMinThreads(200, 200); // 最小工作线程
ThreadPool.SetMaxThreads(500, 500); // 最大工作线程
// 启动ASP.NET Core应用
CreateHostBuilder(args).Build().Run();
}
}
步骤2:在配置文件中启用服务器GC模式
// appsettings.json
{
"GCSettings": {
"LatencyMode": "InteractiveServer", // 服务器GC模式
"Server": true // 启用Server GC
}
}
// 在Startup.cs中应用配置
public void ConfigureServices(IServiceCollection services) {
var gcSettings = Configuration.GetSection("GCSettings").Get<GCSettings>();
GCSettings.LatencyMode = gcSettings.LatencyMode;
GCSettings.Server = gcSettings.Server;
}
效果对比:
// 问题代码:日志服务挂了,主线程卡死
public class LoggingService {
public void LogError(string message) {
// 同步调用日志服务(可能因网络问题阻塞)
var logResponse = HttpClient.PostAsync("http://logs.example.com/api/log", new StringContent(message));
logResponse.Wait(); // 等待直到超时!
}
}
// 调用处:用户注册时记录日志
public void RegisterUser(string email) {
// ... 保存用户 ...
loggingService.LogError("用户注册成功"); // 日志服务挂了,主线程卡住!
}
// ✅ 修复后:异步+超时控制
public class LoggingService {
public async Task LogError(string message) {
try {
await HttpClient.PostAsync("http://logs.example.com/api/log", new StringContent(message))
.ConfigureAwait(false) // 不等待主线程
.TimeoutAfter(2000); // 设置2秒超时
} catch (Exception ex) {
// 本地记录错误,避免二次崩溃
File.AppendAllText("error.log", $"日志发送失败:{ex.Message}\n");
}
}
}
// 调用处:即发即弃
public void RegisterUser(string email) {
// ... 保存用户 ...
_ = Task.Run(async () => await loggingService.LogError("用户注册成功")); // 不阻塞
}
原因:太多“即发即弃”任务挤爆线程池!
解决:
// 在Program.cs中限制线程池最大任务数
ThreadPool.GetMaxThreads(out int worker, out int io);
ThreadPool.SetMaxThreads(worker - 50, io); // 预留50个线程给其他任务
原因:未处理异常让任务“悄无声息”挂掉!
解决:
// 在Task.Run中强制捕获异常
_ = Task.Run(() => {
try {
// ... 任务逻辑 ...
} catch (Exception ex) {
// 记录异常到日志系统
Serilog.Log.Error(ex, "即发即弃任务失败!");
}
});
原因:在ASP.NET Core中同步等待异步任务!
解决:
// 错误示例:同步等待导致死锁
public void DoWork() {
var task = SomeAsyncMethod();
task.Wait(); // 死锁!
}
// ✅ 正确示例:始终用async/await
public async Task DoWork() {
await SomeAsyncMethod().ConfigureAwait(false); // 不等待主线程
}
原因:未正确释放数据库连接或文件句柄!
解决:
// 使用using确保资源释放
_ = Task.Run(() => {
using (var conn = new SqlConnection("...")) {
conn.Open();
// ... 执行查询 ...
} // 自动释放连接
});
原因:CPU密集型任务占用线程池太久!
解决:
// 使用独立线程或后台服务处理长任务
public void StartLongTask() {
var thread = new Thread(() => {
// ... 长任务逻辑 ...
});
thread.IsBackground = true;
thread.Start();
}