编辑
2025-11-23
C#
00

目录

异步编程基础概念
同步与异步的对比
C#中的async/await模型详解
关键概念
基本语法结构
网络编程中的实际应用示例
示例1:异步HTTP请求
异步编程的最佳实践
正确命名异步方法
避免混合同步和异步代码
正确处理异常
使用异步流处理大量数据
总结:网络编程中异步编程的价值

在现代C#网络应用开发中,异步编程已经从"可选技能"变成了"必备技能"。无论是开发Web API、实时通讯应用还是高性能服务,异步编程都能显著提升应用程序的响应性和资源利用率。本文将深入剖析C#中的异步编程模型(async/await),并结合网络编程场景提供详细示例。

异步编程基础概念

同步与异步的对比

在传统的同步编程模型中,代码按顺序执行,当遇到耗时操作时(如网络请求),线程会被阻塞直到操作完成:

C#
// 同步方法示例 public string DownloadWebPage(string url) { // 此处线程被阻塞,直到下载完成 WebClient client = new WebClient(); return client.DownloadString(url); // 阻塞操作 }

而异步编程允许线程在等待耗时操作完成时去执行其他工作:

C#
// 异步方法示例 public async Task<string> DownloadWebPageAsync(string url) { // 异步操作,不会阻塞线程 WebClient client = new WebClient(); return await client.DownloadStringTaskAsync(url); // 非阻塞操作 }

C#中的async/await模型详解

C#的async/await模型是基于Task和Task类型构建的语法糖,大大简化了异步编程。

关键概念

  1. async关键字:标记方法、lambda表达式或匿名方法为异步
  2. await关键字:暂停异步方法的执行,直到等待的任务完成
  3. Task和Task:表示异步操作的对象

基本语法结构

C#
// 无返回值的异步方法 public async Task ProcessDataAsync() { // 异步操作 await Task.Delay(1000); // 模拟耗时操作 Console.WriteLine("处理完成"); } // 有返回值的异步方法 public async Task<int> CalculateAsync() { await Task.Delay(1000); // 模拟耗时计算 return 42; // 返回计算结果 }

网络编程中的实际应用示例

示例1:异步HTTP请求

以下是一个完整的HTTP请求示例,展示了如何使用异步方法进行网络请求:

C#
namespace AppNetworkAsync { internal class Program { // 创建一个具有自定义配置的HttpClient private static readonly HttpClient httpClient = CreateHttpClient(); public static async Task Main() { try { // 调用异步方法并等待结果 string result = await FetchDataAsync("https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=jQuery37105169177110964975_1746321786227&fs=m%3A1%2Bt%3A2%2Cm%3A1%2Bt%3A23&fields=f12%2Cf13%2Cf14%2Cf1%2Cf2%2Cf4%2Cf3%2Cf152%2Cf5%2Cf6%2Cf7%2Cf15%2Cf18%2Cf16%2Cf17%2Cf10%2Cf8%2Cf9%2Cf23&fid=f3&pn=1&pz=20&po=1&dect=1&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1746321786242"); Console.WriteLine($"获取的数据: {result}"); // 异步并行请求示例 Task<string> task1 = FetchDataAsync("https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=jQuery37105169177110964975_1746321786227&fs=m%3A1%2Bt%3A2%2Cm%3A1%2Bt%3A23&fields=f12%2Cf13%2Cf14%2Cf1%2Cf2%2Cf4%2Cf3%2Cf152%2Cf5%2Cf6%2Cf7%2Cf15%2Cf18%2Cf16%2Cf17%2Cf10%2Cf8%2Cf9%2Cf23&fid=f3&pn=2&pz=20&po=1&dect=1&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1746321786242"); Task<string> task2 = FetchDataAsync("https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=jQuery37105169177110964975_1746321786227&fs=m%3A1%2Bt%3A2%2Cm%3A1%2Bt%3A23&fields=f12%2Cf13%2Cf14%2Cf1%2Cf2%2Cf4%2Cf3%2Cf152%2Cf5%2Cf6%2Cf7%2Cf15%2Cf18%2Cf16%2Cf17%2Cf10%2Cf8%2Cf9%2Cf23&fid=f3&pn=3&pz=20&po=1&dect=1&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1746321786242"); // 等待所有请求完成 await Task.WhenAll(task1, task2); Console.WriteLine($"第一个API结果: {task1.Result}"); Console.WriteLine($"第二个API结果: {task2.Result}"); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } // 创建配置好的HttpClient private static HttpClient CreateHttpClient() { var client = new HttpClient(); // 设置默认请求头,模拟浏览器 client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"); client.DefaultRequestHeaders.Add("Accept", "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"); client.DefaultRequestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); client.DefaultRequestHeaders.Add("Referer", "https://finance.pae.baidu.com/"); // 设置超时时间 client.Timeout = TimeSpan.FromSeconds(30); return client; } // 异步获取网络数据的方法 public static async Task<string> FetchDataAsync(string url) { Console.WriteLine($"开始请求URL: {url}"); try { // 创建请求消息 var request = new HttpRequestMessage(HttpMethod.Get, url); // 发送异步HTTP请求,并使用超时令牌 using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30))) { HttpResponseMessage response = await httpClient.SendAsync(request, cts.Token); // 确保HTTP请求成功 response.EnsureSuccessStatusCode(); // 异步读取响应内容 string content = await response.Content.ReadAsStringAsync(); Console.WriteLine($"完成请求URL: {url}"); return content; } } catch (TaskCanceledException) { Console.WriteLine($"请求URL超时: {url}"); throw new TimeoutException($"请求URL超时: {url}"); } catch (Exception ex) { Console.WriteLine($"请求URL出错: {url}, 错误: {ex.Message}"); throw; } } } }

image.png

异步编程的最佳实践

正确命名异步方法

遵循微软推荐的命名约定,在异步方法名后添加"Async"后缀:

C#
// 正确的命名 public async Task<string> DownloadFileAsync(string url) { // 异步实现 } // 不推荐的命名 public async Task<string> DownloadFile(string url) { // 异步实现 }

避免混合同步和异步代码

C#
// 错误示例:在异步方法中使用同步调用 public async Task ProcessDataAsync() { // 错误:在异步方法中使用同步阻塞调用 string data = GetDataSync(); // 同步方法 await ProcessAsync(data); } // 正确示例 public async Task ProcessDataAsync() { // 正确:全程使用异步调用 string data = await GetDataAsync(); await ProcessAsync(data); }

正确处理异常

C#
public async Task DownloadAndProcessAsync(string url) { try { var data = await DownloadAsync(url); await ProcessAsync(data); } catch (HttpRequestException ex) { // 处理网络异常 Console.WriteLine($"下载失败: {ex.Message}"); } catch (Exception ex) { // 处理其他异常 Console.WriteLine($"处理失败: {ex.Message}"); } }

使用异步流处理大量数据

C# 8.0引入的异步流(IAsyncEnumerable)适用于处理大量数据:

C#
using System; using System.Collections.Generic; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using System.Text.RegularExpressions; namespace AppNetworkAsync { internal class Program { public static async Task Main() { try { // 正确消费异步流 await foreach (var stockName in GetLargeDataSetAsync()) { Console.WriteLine(stockName); } Console.WriteLine("所有数据处理完成"); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } // 产生异步流的方法 public static async IAsyncEnumerable<string> GetLargeDataSetAsync() { using HttpClient client = new HttpClient(); // 模拟分页请求 for (int page = 1; page <= 10; page++) { Console.WriteLine($"正在获取第 {page} 页数据..."); // 异步获取每一页数据 string url = $"https://push2.eastmoney.com/api/qt/clist/get?np=1&fltt=1&invt=2&cb=jQuery37105169177110964975_1746321786227&fs=m%3A1%2Bt%3A2%2Cm%3A1%2Bt%3A23&fields=f12%2Cf13%2Cf14%2Cf1%2Cf2%2Cf4%2Cf3%2Cf152%2Cf5%2Cf6%2Cf7%2Cf15%2Cf18%2Cf16%2Cf17%2Cf10%2Cf8%2Cf9%2Cf23&fid=f3&pn={page}&pz=20&po=1&dect=1&ut=fa5fd1943c7b386f172d6893dbfba10b&wbp2u=%7C0%7C0%7C0%7Cweb&_=1746321786242"; string pageData; try { pageData = await client.GetStringAsync(url); } catch (Exception ex) { Console.WriteLine($"获取数据失败: {ex.Message}"); continue; // 跳过这一页,继续下一页 } // 在继续之前添加一点延迟,避免请求过快 await Task.Delay(500); // 处理并返回数据项 foreach (var item in ParsePageData(pageData)) { yield return item; } } } // 辅助方法:解析页面数据 private static IEnumerable<string> ParsePageData(string pageData) { List<string> results = new List<string>(); // 东方财富的数据通常是JSONP格式,需要提取JSON部分 var match = Regex.Match(pageData, @"jQuery\d+_\d+\((.*)\)"); if (!match.Success) { return new[] { "数据格式不符合预期" }; } string jsonData = match.Groups[1].Value; JsonDocument doc = null; try { // 解析JSON数据 doc = JsonDocument.Parse(jsonData); // 提取股票数据数组 if (doc.RootElement.TryGetProperty("data", out JsonElement dataElement) && dataElement.TryGetProperty("diff", out JsonElement diffElement)) { foreach (JsonElement stockElement in diffElement.EnumerateArray()) { // 只获取股票名称(f14) if (stockElement.TryGetProperty("f14", out JsonElement nameElement)) { string name = nameElement.GetString() ?? "未知名称"; results.Add(name); } } } } catch (JsonException) { results.Add("JSON解析失败"); } finally { doc?.Dispose(); } return results; } } }

image.png

总结:网络编程中异步编程的价值

C#的async/await模型为网络编程带来了显著优势:

  1. 提高响应性:异步操作不会阻塞UI线程或Web服务器线程
  2. 提升吞吐量:更有效地利用可用系统资源
  3. 简化错误处理:异常处理模型与同步代码一致
  4. 改善代码可读性:比旧的基于回调的异步编程模型更直观
  5. 更好地支持可扩展性:服务器能够处理更多并发连接

掌握C#异步编程是现代.NET开发者的必备技能,特别是在网络编程领域。正确使用async/await模式可以显著提升应用程序的性能和用户体验。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!