Task和异步方法

一、Task 实例化方式及启动(含代码示例)

1. new Task() + Start()(冷任务,需手动启动)
  • 特点:通过 new Task() 创建的是 "冷任务"(未启动),必须调用 Start() 才会执行。

  • 支持 Action(无返回值)和 Action<T>(带参数)委托。

// 1.1 无参数无返回值
Task task1 = new Task(() => {
    Console.WriteLine("任务1执行:无参数无返回值");
});
task1.Start(); // 必须手动启动
task1.Wait(); // 等待任务完成
​
​
// 1.2 带参数(通过 Action<object> 传递,需装箱)
Task task2 = new Task((obj) => {
    string message = obj as string;
    Console.WriteLine($"任务2执行:参数={message}");
}, "Hello Task"); // 第二个参数为传递的数据
task2.Start();
task2.Wait();
2. Task.Run()(热任务,自动启动,推荐)
  • 特点:.NET 4.5 推出,简化任务创建,直接在 ThreadPool 上启动 "热任务"(无需手动 Start)。

  • 支持 Func<TResult>(带返回值)和 Action(无返回值)。

// 2.1 无返回值
Task task3 = Task.Run(() => {
    Console.WriteLine("任务3执行:Task.Run 无返回值");
});
task3.Wait();
​
​
// 2.2 带返回值(Task<T>)
Task<int> task4 = Task.Run(() => {
    return 100 + 200; // 返回结果
});
int result4 = task4.Result; // 获取返回值(会阻塞,推荐用 await)
Console.WriteLine($"任务4结果:{result4}");
3. Task.Factory.StartNew()(热任务,功能更灵活)
  • 特点:通过工厂模式创建任务,可配置任务选项(如是否长期运行、调度方式等)。

  • 适合复杂场景(如指定 TaskCreationOptions)。

// 3.1 基础用法(自动启动)
Task task5 = Task.Factory.StartNew(() => {
    Console.WriteLine("任务5执行:Factory.StartNew");
});
task5.Wait();
​
​
// 3.2 带返回值 + 配置选项
Task<string> task6 = Task.Factory.StartNew(() => {
    return "任务6的返回值";
}, TaskCreationOptions.LongRunning); // 标记为长期运行(可能创建新线程而非用ThreadPool)
string result6 = await task6; // 推荐用 await 获取结果
Console.WriteLine($"任务6结果:{result6}");

二、向任务传递数据(委托的使用)

  • 核心:通过委托的参数传递(Action<object> 或泛型委托)。

  • 推荐:复杂参数用自定义类封装,避免多次装箱。

// 示例:传递复杂参数
public class TaskParam {
    public int Id { get; set; }
    public string Name { get; set; }
}
​
// 使用 Action<object> 接收参数
Task task7 = Task.Run(() => {
    TaskParam param = new TaskParam { Id = 1, Name = "Test" };
    ProcessParam(param);
});
​
// 处理参数的方法
static void ProcessParam(TaskParam param) {
    Console.WriteLine($"处理参数:Id={param.Id}, Name={param.Name}");
}

三、任务管理(取消任务)

  • 核心类:CancellationTokenSource(CTS)和 CancellationToken(令牌)。

  • 流程:创建 CTS → 传递令牌给任务 → 任务中检查取消请求 → 调用 Cancel() 触发取消。

// 创建取消令牌源
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
​
// 启动可取消的任务
Task task8 = Task.Run(() => {
    for (int i = 0; i < 10; i++) {
        // 检查是否取消请求(IsCancellationRequested)
        if (token.IsCancellationRequested) {
            Console.WriteLine("任务8已取消");
            token.ThrowIfCancellationRequested(); // 可选:抛出异常标记取消
            break;
        }
        Console.WriteLine($"任务8执行中:{i}");
        Thread.Sleep(100);
    }
}, token); // 传递令牌
​
// 3秒后取消任务
Task.Run(async () => {
    await Task.Delay(3000);
    cts.Cancel(); // 触发取消
});
​
try {
    await task8;
} catch (OperationCanceledException) {
    Console.WriteLine("任务8被取消(捕获异常)");
} finally {
    cts.Dispose(); // 释放资源
}

四、任务等待(同步 / 异步等待)

方法作用特点(是否阻塞)
task.Wait()等待单个任务完成阻塞当前线程
Task.WaitAll(tasks)等待所有任务完成阻塞当前线程
Task.WaitAny(tasks)等待任一任务完成阻塞当前线程
Task.WhenAll(tasks)异步等待所有任务完成(返回 Task)不阻塞,需配合 await
Task.WhenAny(tasks)异步等待任一任务完成(返回 Task<Task>)不阻塞,需配合 await
// 示例:异步等待(推荐,不阻塞)
Task taskA = Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("A完成"); });
Task taskB = Task.Run(() => { Thread.Sleep(2000); Console.WriteLine("B完成"); });
​
// 等待所有任务完成(异步,不阻塞)
await Task.WhenAll(taskA, taskB);
Console.WriteLine("A和B都完成了");
​
// 等待任一任务完成
Task firstCompleted = await Task.WhenAny(taskA, taskB);
Console.WriteLine($"先完成的任务:{firstCompleted.Id}");

五、任务返回值(Task<T>

  • 泛型任务 Task<T> 用于获取返回值,通过 Result 属性(阻塞)或 await(非阻塞)获取。

// 带返回值的任务
Task<int> sumTask = Task.Run(() => {
    int sum = 0;
    for (int i = 1; i <= 100; i++) sum += i;
    return sum;
});
​
// 正确方式:用 await 获取(非阻塞)
int sum = await sumTask;
Console.WriteLine($"1-100的和:{sum}");
​
// 不推荐:用 Result 获取(会阻塞当前线程)
// int sum = sumTask.Result; 

六、任务中的异常处理

  • 任务中的异常会被封装在 Task 中,只有在等待时(Wait()/await)才会抛出。

  • 需用 try-catch 捕获,多任务异常需通过 AggregateException 处理。

// 单个任务异常
Task errorTask = Task.Run(() => {
    throw new InvalidOperationException("任务内部出错了!");
});
​
try {
    await errorTask;
} catch (InvalidOperationException ex) {
    Console.WriteLine($"捕获异常:{ex.Message}");
}
​
​
// 多个任务异常(AggregateException)
Task taskX = Task.Run(() => { throw new Exception("X错误"); });
Task taskY = Task.Run(() => { throw new Exception("Y错误"); });
​
try {
    await Task.WhenAll(taskX, taskY);
} catch (AggregateException ex) {
    // 遍历所有内部异常
    foreach (var innerEx in ex.InnerExceptions) {
        Console.WriteLine($"任务异常:{innerEx.Message}");
    }
}

七、async/await 核心总结

  1. 定义异步方法:用 async 修饰方法,返回值通常为 Task(无返回值)或 Task<T>(有返回值)。

    async Task<string> GetDataAsync() {
        // 异步操作(如IO、网络请求)
        await Task.Delay(1000); // 模拟耗时操作
        return "异步数据";
    }
  2. await 的作用

    • 等待 Task/Task<T> 完成,但不阻塞当前线程(线程会返回执行其他工作)。

    • 只能在 async 方法中使用。

  3. 核心优势

    • 以 "同步代码的写法" 实现异步逻辑,可读性强。

    • 避免回调嵌套(回调地狱),保证代码执行顺序。

    • 不阻塞 UI 线程(如 WPF/WinForm),提升用户体验。

总结

  • Task 是.NET 异步编程的核心,推荐用 Task.Run()Task.Factory.StartNew() 创建热任务。

  • 任务取消依赖 CancellationTokenSource,需在任务中主动检查取消请求。

  • 等待任务优先用 await Task.WhenAll()/WhenAny()(非阻塞),避免 Wait()(阻塞)。

  • async/await 简化异步代码,让异步逻辑像同步代码一样直观,是现代 C# 异步编程的首选方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张謹礧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值