C#Task.ContinueWith 方法深度解析

ContinueWith 是 TPL (Task Parallel Library) 中用于任务延续的重要方法,它允许在一个任务完成后自动执行后续操作。下面我将全面解析其用法和最佳实践。

一、基本语法

public Task ContinueWith(
    Action<Task> continuationAction,
    TaskScheduler scheduler = null,
    TaskContinuationOptions continuationOptions = TaskContinuationOptions.None,
    CancellationToken cancellationToken = default
)

二、核心功能

  1. 任务链式执行:在前置任务完成后自动触发后续操作

  2. 多任务协调:处理多个任务的执行顺序和依赖关系

  3. 异常传播:自动处理前置任务的异常状态

三、常用重载方法

1. 基本延续

task.ContinueWith(previousTask => { // 前置任务完成后执行的代码 });

2. 指定任务调度器

task.ContinueWith(previousTask => {
    // UI线程更新
}, TaskScheduler.FromCurrentSynchronizationContext());

3. 带返回值的延续

Task<string> continuation = task.ContinueWith(previousTask => {
    return "处理结果: " + previousTask.Result;
});

4. 带条件选项的延续

task.ContinueWith(previousTask => {
    // 只在任务成功完成时执行
}, TaskContinuationOptions.OnlyOnRanToCompletion);

四、关键参数详解

1. TaskContinuationOptions 常用选项

选项描述
None默认选项,无论前置任务状态如何都会执行
OnlyOnRanToCompletion仅在前置任务成功完成时执行
OnlyOnFaulted仅在前置任务出错时执行
OnlyOnCanceled仅在前置任务被取消时执行
NotOnRanToCompletion前置任务未成功完成时执行
ExecuteSynchronously尝试同步执行延续任务

2. TaskScheduler 选择

  • TaskScheduler.Default:线程池调度器

  • TaskScheduler.FromCurrentSynchronizationContext():UI线程调度器

  • TaskScheduler.Current:当前任务的调度器

五、典型使用场景

1. UI线程更新

Task.Run(() => {
    // 后台工作
}).ContinueWith(t => {
    textBox1.Text = t.Result; // 错误!跨线程访问
}, TaskScheduler.FromCurrentSynchronizationContext()); // 正确方式

2. 异常处理

Task.Run(() => {
    throw new Exception("测试异常");
}).ContinueWith(t => {
    if (t.IsFaulted) {
        Console.WriteLine($"异常: {t.Exception.InnerException.Message}");
    }
});

3. 多任务串联

Task.Run(() => 10)
    .ContinueWith(t => t.Result * 2)
    .ContinueWith(t => t.Result + 5)
    .ContinueWith(t => Console.WriteLine($"最终结果: {t.Result}"));

六、与async/await对比

特性ContinueWithasync/await
代码风格回调式线性同步式
异常处理需检查IsFaulted直接try-catch
上下文恢复需手动指定自动恢复
可读性较低较高
灵活性更高相对固定

七、最佳实践

  1. 始终处理异常

    task.ContinueWith(t => {
        if (t.IsFaulted) { /* 处理异常 */ }
    }, TaskContinuationOptions.OnlyOnFaulted);
  2. 避免嵌套过深

    // 不推荐
    task.ContinueWith(t1 => {
        t1.ContinueWith(t2 => {
            t2.ContinueWith(t3 => { /* ... */ });
        });
    });
    
    // 推荐
    task.ContinueWith(t1 => { /* ... */ })
        .ContinueWith(t2 => { /* ... */ })
        .ContinueWith(t3 => { /* ... */ });
  3. 资源清理

    var cts = new CancellationTokenSource();
    Task.Run(() => { /* ... */ }, cts.Token)
        .ContinueWith(t => {
            cts.Dispose(); // 及时释放资源
        });
  4. 性能优化

    // 对于简单延续,考虑同步执行
    task.ContinueWith(t => Console.WriteLine("完成"),
        TaskContinuationOptions.ExecuteSynchronously);

八、常见问题解决方案

  1. 跨线程访问UI控件

    Task.Run(() => 42).ContinueWith(t => {
        label1.Invoke((Action)(() => label1.Text = t.Result.ToString()));
    });
  2. 取消延续任务

    var cts = new CancellationTokenSource();
    Task.Run(() => { /* ... */ })
        .ContinueWith(_ => { /* ... */ }, cts.Token);
    
    // 取消延续
    cts.Cancel();
  3. 处理AggregateException

    task.ContinueWith(t => {
        if (t.Exception != null) {
            foreach (var ex in t.Exception.InnerExceptions) {
                Console.WriteLine(ex.Message);
            }
        }
    });

ContinueWith 提供了强大的任务延续能力,但在现代C#开发中,大多数场景下推荐优先使用 async/await,它提供了更简洁直观的代码结构。保留 ContinueWith 用于需要更精细控制任务调度的复杂场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值