1. C#多线程编程实战指南
1.1 线程基础与Thread类
在C#中创建线程最基础的方式是使用System.Threading.Thread类。我通常会这样初始化一个工作线程:
csharp复制Thread workerThread = new Thread(new ThreadStart(DoWork));
workerThread.Start();
void DoWork()
{
Console.WriteLine($"线程ID:{Thread.CurrentThread.ManagedThreadId} 开始执行");
// 模拟耗时操作
Thread.Sleep(2000);
Console.WriteLine("工作线程完成任务");
}
这里有几个关键点需要注意:
- 线程启动后立即返回,不会阻塞主线程
- 每个线程都有独立的调用栈和局部变量
- 默认情况下创建的线程是前台线程(会阻止进程退出)
重要提示:直接使用Thread类在需要大量线程时会产生显著性能开销,因为每个Thread都对应一个操作系统线程。
1.2 线程池的最佳实践
对于短期任务,更推荐使用线程池(ThreadPool):
csharp复制ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine($"线程池线程ID:{Thread.CurrentThread.ManagedThreadId}");
// 执行任务...
});
线程池的特点:
- 自动管理线程生命周期
- 限制最大并发线程数(默认等于CPU核心数)
- 适合执行短时任务(<250ms)
我在实际项目中发现,当任务执行时间超过1秒时,使用Task会比直接使用线程池更合适。
1.3 Task并行库深度解析
Task Parallel Library (TPL) 是现代C#多线程编程的首选方案:
csharp复制Task.Run(() =>
{
Console.WriteLine($"Task线程ID:{Thread.CurrentThread.ManagedThreadId}");
// CPU密集型工作
}).ContinueWith(t =>
{
// 后续处理
});
TPL的优势包括:
- 更丰富的任务组合方式(ContinueWith, WhenAll等)
- 内置取消支持(CancellationToken)
- 更好的异常处理机制
一个常见的错误是过度创建Task。根据我的经验,对于IO密集型操作,应该优先考虑async/await模式。
1.4 线程同步与锁机制
当多个线程访问共享资源时,必须考虑同步问题。以下是几种常用方案:
csharp复制// 1. lock语句(最常用)
private static readonly object _lockObj = new object();
lock(_lockObj)
{
// 临界区代码
}
// 2. Monitor类(lock的底层实现)
Monitor.Enter(_lockObj);
try {
// 临界区代码
}
finally {
Monitor.Exit(_lockObj);
}
// 3. Mutex(跨进程同步)
using var mutex = new Mutex(false, "Global\\MyMutex");
mutex.WaitOne();
try {
// 临界区代码
}
finally {
mutex.ReleaseMutex();
}
经验之谈:锁的粒度要尽可能小,持有时间要尽可能短。我曾遇到过一个性能问题,最终发现是因为锁住了整个数据库访问方法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C#网络编程核心技术
2.1 Socket编程基础
C#提供了System.Net.Sockets命名空间进行底层网络编程。一个简单的TCP服务器实现:
csharp复制TcpListener server = new TcpListener(IPAddress.Any, 8080);
server.Start();
while (true)
{
TcpClient client = server.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
void HandleClient(object state)
{
using var client = (TcpClient)state;
using var stream = client.GetStream();
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
// 处理请求...
}
关键注意事项:
- 每个客户端连接应该使用独立线程/任务处理
- 必须考虑网络字节序问题
- 要处理连接异常和超时
2.2 高性能网络IO模式
现代网络编程更推荐使用异步模式:
csharp复制async Task StartAsyncServer()
{
TcpListener server = new TcpListener(IPAddress.Any, 8080);
server.Start();
while (true)
{
var client = await server.AcceptTcpClientAsync();
_ = HandleClientAsync(client); // 丢弃任务但不等待
}
}
async Task HandleClientAsync(TcpClient client)
{
using (client)
using (var stream = client.GetStream())
{
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
// 处理请求...
}
}
这种模式的优势:
- 可扩展性强(可处理数千并发连接)
- 资源利用率高(不需要每个连接一个线程)
- 代码结构更清晰
2.3 HTTP客户端实践
对于HTTP通信,HttpClient是更好的选择:
csharp复制using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://api.example.com");
// GET请求示例
var response = await httpClient.GetAsync("/users");
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
// 处理响应...
}
// POST请求示例
var json = JsonSerializer.Serialize(new { Name = "John" });
var postResponse = await httpClient.PostAsync("/users",
new StringContent(json, Encoding.UTF8, "application/json"));
重要提示:
- HttpClient应该重用而不是每次创建新实例
- 考虑使用IHttpClientFactory管理生命周期
- 设置合理的Timeout和MaxConnectionsPerServer
3. LINQ技术深度解析
3.1 LINQ基础语法
LINQ(Language Integrated Query)是C#中最强大的特性之一。基本查询语法:
csharp复制var numbers = new List<int> { 1, 2, 3, 4, 5 };
// 查询表达式语法
var evenNumbers = from n in numbers
where n % 2 == 0
select n;
// 方法链语法
var evenNumbers = numbers.Where(n => n % 2 == 0);
LINQ的核心优势:
- 统一的查询模型(无论数据源是集合、SQL还是XML)
- 编译时类型检查
- 延迟执行(查询定义和执行分离)
3.2 高级LINQ操作
实际项目中常用的复杂操作:
csharp复制// 分组
var grouped = products.GroupBy(p => p.Category);
// 连接
var joined = from p in products
join s in suppliers on p.SupplierID equals s.ID
select new { p.Name, s.Name };
// 聚合
var stats = new {
Count = products.Count(),
Total = products.Sum(p => p.Price),
Avg = products.Average(p => p.Price)
};
性能提示:
- 对于大型集合,考虑使用AsParallel()进行并行查询
- 多次使用的查询结果应该缓存(ToList()/ToArray())
- 注意IEnumerable的多次枚举问题
3.3 LINQ to SQL实战
将LINQ用于数据库访问的典型模式:
csharp复制using var db = new DataContext(connectionString);
// 查询
var expensiveProducts = from p in db.Products
where p.Price > 100
select p;
// 插入
var newProduct = new Product { Name = "New Item", Price = 50 };
db.Products.InsertOnSubmit(newProduct);
db.SubmitChanges();
// 更新
var product = db.Products.First(p => p.ID == 1);
product.Price *= 1.1m;
db.SubmitChanges();
最佳实践:
- 使用using确保DbContext正确释放
- 批量操作时考虑性能(避免N+1查询问题)
- 复杂查询应该检查生成的SQL语句
4. 综合应用与性能优化
4.1 多线程网络服务架构
结合多线程和网络编程的典型架构:
csharp复制async Task RunServer()
{
var listener = new TcpListener(IPAddress.Any, 8080);
listener.Start();
var tasks = new List<Task>();
var cts = new CancellationTokenSource();
// 接受连接循环
while (!cts.Token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync();
tasks.Add(HandleClientAsync(client, cts.Token));
}
await Task.WhenAll(tasks);
}
async Task HandleClientAsync(TcpClient client, CancellationToken ct)
{
try {
using (client)
using (var stream = client.GetStream())
{
// 处理请求...
}
}
catch (Exception ex) {
// 日志记录...
}
}
架构要点:
- 使用CancellationToken实现优雅关闭
- 错误处理要完善(网络操作可能随时失败)
- 考虑引入连接限制和超时控制
4.2 性能调优技巧
从实际项目中总结的优化经验:
- 线程池配置:
csharp复制ThreadPool.SetMinThreads(100, 100); // 对于高并发IO密集型应用
- 缓冲区管理:
csharp复制// 使用ArrayPool减少GC压力
var buffer = ArrayPool<byte>.Shared.Rent(1024);
try {
// 使用buffer...
}
finally {
ArrayPool<byte>.Shared.Return(buffer);
}
- LINQ性能陷阱:
csharp复制// 不好的做法:多次枚举
var results = items.Where(SomeCondition);
if (results.Any()) {
Process(results.ToList()); // 第二次枚举
}
// 好的做法:缓存结果
var results = items.Where(SomeCondition).ToList();
if (results.Count > 0) {
Process(results);
}
4.3 调试与诊断
多线程和网络编程的调试技巧:
- 线程命名:
csharp复制Thread.CurrentThread.Name = "Main UI Thread";
- 异步调试:
- 在Visual Studio中使用"Parallel Stacks"窗口
- 使用"Tasks"窗口查看所有运行中的任务
- 网络跟踪:
csharp复制// 启用网络跟踪
System.Diagnostics.Trace.Listeners.Add(
new TextWriterTraceListener("network.log"));
System.Diagnostics.Trace.AutoFlush = true;
我在实际项目中发现,良好的日志记录是诊断复杂并发问题的关键。建议为每个重要操作添加足够的上下文信息。
