关于C#编程中的async / await关键字

关于C#编程中的async / await关键字


一句话理解

async/await 本质是:用"同步写法"写"异步代码",避免线程被阻塞。


核心

  • async:告诉编译器------这个方法里"可能会停下来等一会儿"
  • await :真的"停下来等",但不占用线程
  • 等待期间:线程被释放,可以去干别的事
  • 等结果好了:自动从 await 后一行继续执行

👉 不是多线程魔法,而是状态机 + 回调的语法糖


最基础示例

csharp 复制代码
static async Task Main()
{
    Console.WriteLine("开始");
    await Task.Delay(2000);   // 模拟耗时操作(2秒)
    Console.WriteLine("结束");
}

执行过程

  1. 打印「开始」
  2. 遇到 await
    → 方法"挂起",线程被释放
  3. 2 秒后恢复执行
  4. 打印「结束」

重点:这 2 秒内,线程没被卡死


对比:同步 vs async

❌ 同步(阻塞线程)

csharp 复制代码
Thread.Sleep(2000); // 线程被卡住

✅ 异步(不阻塞)

csharp 复制代码
await Task.Delay(2000); // 线程释放

模拟真实场景示例

csharp 复制代码
async Task<string> DownloadAsync()
{
    using var client = new HttpClient();
    return await client.GetStringAsync("https://example.com");
}

调用:

csharp 复制代码
string html = await DownloadAsync();

你看到的是同步写法

底层其实是 异步 + 回调 + 状态机


async / await 的 3 条铁律

  1. await 只能等 Task / Task
  2. async 方法几乎都返回 Task
  3. 一路 async,就一路 await(不要中途 .Result

最容易踩的坑 ❌

csharp 复制代码
var result = DownloadAsync().Result; // ❌ 可能死锁

✅ 正确写法:

csharp 复制代码
var result = await DownloadAsync();

一句话总结

async/await 不是让代码更快,而是让程序不卡

特别适合:UI、IO、网络、数据库、上位机通信

用 .NET Framework 4.8 + WPF(.NET Framework 4.8)

核心目的就一个:直观看到"同步会卡 UI,async/await 不会"。

WPF 示例(.NET Framework 4.8)

XAML(MainWindow.xaml)

xml 复制代码
<Window x:Class="WpfAsyncDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Async Demo" Height="200" Width="360">
    <Grid Margin="12">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Margin="0,0,0,12">
            <Button Width="120" Margin="0,0,12,0" Content="同步卡死" Click="BtnSync_Click"/>
            <Button Width="120" Content="Async不阻塞" Click="BtnAsync_Click"/>
        </StackPanel>

        <TextBlock Grid.Row="1" FontSize="16" Text="{Binding Status}" />
    </Grid>
</Window>

代码(MainWindow.xaml.cs)

csharp 复制代码
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfAsyncDemo
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _status = "状态";
        public string Status
        {
            get => _status;
            set { _status = value; OnPropertyChanged(); }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        // ❌ 同步:阻塞UI线程,窗口假死
        private void BtnSync_Click(object sender, RoutedEventArgs e)
        {
            Status = "同步开始(UI将卡住3秒)";
            Thread.Sleep(3000);
            Status = "同步结束";
        }

        // ✅ 异步:不阻塞UI线程
        private async void BtnAsync_Click(object sender, RoutedEventArgs e)
        {
            Status = "异步开始(UI仍然流畅)";
            await Task.Delay(3000);
            Status = "异步结束";
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string name = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}

关键点

  • WPF 的按钮点击事件都在 UI线程
  • Thread.Sleep / 同步IO 会把 UI线程卡住 → 窗口假死
  • await Task.Delay/await IO让出 UI线程 → 窗口继续响应
  • 默认情况下:await 之后会回到 UI线程(所以你可以直接更新 Label/TextBlock,不用 Invoke)

进阶:模拟"真实耗时IO/计算"

你如果要做"上位机通信/数据库/文件",通常是:

  • IO:直接 await(比如 HttpClient、Stream.ReadAsync、SqlCommand.ExecuteReaderAsync)
  • CPU计算:用 Task.Run 扔到线程池

给你一个通用模板(WinForms/WPF 都能用):

csharp 复制代码
private async void BtnWork_Click(object sender, EventArgs e)
{
    Status = "开始干活...";
    var result = await Task.Run(() =>
    {
        // CPU密集:放这里
        Thread.Sleep(3000);
        return "完成";
    });
    Status = result;
}
相关推荐
独自破碎E4 小时前
LCR001-两数相除
java·开发语言
70asunflower4 小时前
Python网络内容下载框架教程
开发语言·网络·python
微祎_4 小时前
Flutter for OpenHarmony:构建一个专业级 Flutter 番茄钟,深入解析状态机、定时器管理与专注力工具设计
开发语言·javascript·flutter
Eiceblue4 小时前
通过 C# 解析 HTML:文本提取 + 结构化数据获取
c#·html·.net·visual studio
2401_891450464 小时前
C++中的职责链模式实战
开发语言·c++·算法
m0_708830965 小时前
C++中的原型模式变体
开发语言·c++·算法
薯片锅巴5 小时前
锅巴的JavaScript进阶修炼日记2:面向对象编程/原型及原型链
开发语言·javascript·ecmascript
热爱编程的小刘5 小时前
Lesson02---类与对象(上篇)
开发语言·c++
!停5 小时前
数据结构时间复杂度
c语言·开发语言·算法
一叶星殇5 小时前
.NET 6 NLog 实现多日志文件按业务模块拆分的实践
开发语言·.net