当前位置: 首页 > article >正文

c#异步编程(async/await)

注:下文摘自ChatGPT,总结与案例都非常完善,可以快速理解并应用

0:使用场景

在winform界面程序中,在ui操作中涉及到一些耗时的等待操作,使用线程自己处理已经显得力不从心,如何能更好的实现:能等待后台线程执行完,而且不阻塞UI。c#提供如下操作,高效满足此类应用场景,c++中同样有类似的用法(类似于QT线程间通信(信号槽))

1. 使用异步编程(async/await

最推荐的解决方法是使用 async/await,它可以将耗时操作放到后台线程运行,同时保持 UI 线程的响应。

示例:

场景:按钮点击后等待一个耗时任务完成,但界面仍可响应。
private async void Button_Click(object sender, EventArgs e)
{
    // 显示加载状态
    Button.Enabled = false;
    Label.Text = "Processing...";

    // 执行耗时操作
    await Task.Run(() => LongRunningTask());

    // 恢复 UI
    Label.Text = "Done!";
    Button.Enabled = true;
}

// 模拟一个耗时任务
private void LongRunningTask()
{
    Thread.Sleep(5000); // 模拟耗时操作,实际中可替换为真实逻辑
}

原理

  • Task.Run 将耗时操作放到线程池中运行,不阻塞主线程。
  • await 确保任务完成后才继续执行,但不阻塞 UI 线程。

2. 使用 BackgroundWorker

BackgroundWorker 是较老的异步模式,但仍可用于一些简单场景。

示例:

private BackgroundWorker worker;

public MainForm()
{
    InitializeComponent();
    worker = new BackgroundWorker();
    worker.DoWork += Worker_DoWork;
    worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
}

private void Button_Click(object sender, EventArgs e)
{
    // 显示加载状态
    Button.Enabled = false;
    Label.Text = "Processing...";
    
    // 启动后台任务
    worker.RunWorkerAsync();
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    // 在后台线程中执行耗时操作
    Thread.Sleep(5000); // 模拟耗时操作
}

private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // 任务完成后更新 UI
    Label.Text = "Done!";
    Button.Enabled = true;
}

注意

  • DoWork 方法在后台线程中运行。
  • RunWorkerCompleted 方法在主线程(UI 线程)中运行,适合更新界面。

3. 使用 Task 和 Lambda

如果你不想使用 async/await,可以直接使用 Task 和回调来处理任务。

示例:

private void Button_Click(object sender, EventArgs e)
{
    // 显示加载状态
    Button.Enabled = false;
    Label.Text = "Processing...";

    Task.Run(() =>
    {
        // 在后台线程中执行耗时操作
        Thread.Sleep(5000); // 模拟耗时操作
    }).ContinueWith(t =>
    {
        // 回到主线程更新 UI
        Label.Text = "Done!";
        Button.Enabled = true;
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

4. 使用 DispatcherInvoke

在 WPF 中,可以使用 Dispatcher 更新 UI;在 WinForms 中,可以使用 Invoke 方法。

示例(WinForms):

private void Button_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    {
        // 耗时操作
        Thread.Sleep(5000);

        // 使用 Invoke 更新 UI
        this.Invoke((Action)(() =>
        {
            Label.Text = "Done!";
            Button.Enabled = true;
        }));
    });

    // 立即禁用按钮(UI 线程)
    Button.Enabled = false;
    Label.Text = "Processing...";
}

5. 使用 Progress<T>(可选,报告进度)

如果耗时任务需要报告进度,可以使用 IProgress<T>Progress<T>

示例:

private async void Button_Click(object sender, EventArgs e)
{
    var progress = new Progress<int>(value =>
    {
        // 更新进度条
        ProgressBar.Value = value;
    });

    // 显示加载状态
    Button.Enabled = false;
    Label.Text = "Processing...";

    // 执行耗时任务并报告进度
    await Task.Run(() => LongRunningTaskWithProgress(progress));

    // 恢复 UI
    Label.Text = "Done!";
    Button.Enabled = true;
}

private void LongRunningTaskWithProgress(IProgress<int> progress)
{
    for (int i = 0; i <= 100; i += 10)
    {
        Thread.Sleep(500); // 模拟任务
        progress.Report(i); // 报告进度
    }
}

优点

  • 允许在任务进行过程中更新 UI(如进度条)。

6. 注意事项

  • 避免直接使用 Thread.Sleep 在主线程中运行:会导致 UI 完全无响应。
  • 耗时操作不要在 UI 线程中运行:始终将耗时逻辑放到后台线程。
  • 推荐使用现代的 async/await:代码更简洁且易于维护。

http://www.kler.cn/a/411957.html

相关文章:

  • Pytorch使用手册-Automatic Differentiation with torch.autograd(专题六)
  • win10中使用ffmpeg和MediaMTX 推流rtsp视频
  • Linux和Ubuntu的关系
  • 零基础3分钟快速掌握 ——Linux【终端操作】及【常用指令】Ubuntu
  • 小程序基础:流程。
  • 路由器中继与桥接
  • 阿里云多账号统一认证
  • 玛哈特矫平机:精密制造中的平整大师
  • 多模态大型语言模型(MLLM)综述
  • 微信小程序数据请求教程:GET与POST请求详解
  • Centos 7 系统 openGauss 3.1.0 一主两备集群安装部署指南
  • vue config 接口地址配置
  • Sklearn 内置数据集简介
  • Vue ECharts 基本数据图表绘制详解:让数据飞起来
  • Vue前端开发2.3.2-4 绑定指令
  • C++设计模式-模板模式,Template Method
  • 美国网络安全和基础设施安全局 发布首部国际战略规划
  • 802.15.4 WPAN协议-MAC帧结构
  • Android显示系统(01)- 架构分析
  • Leetcode3206:交替组 I
  • 实现List接口的三类-ArrayList -Vector -LinkedList
  • zabbix 图形中文显示乱码问题
  • 前半有序的排序及有序游标
  • 【SpringBoot】Spring Data Redis的环境搭建(win10)
  • 3D建筑模型的 LOD 规范
  • 非协议默认端口的:NAT alg需配置port-mapping