如何并行运行两个返回Task的方法?代码同步运行问题排查与解决
问题分析与修正
错误点
- 方法无真正并行逻辑:
Method和Info都是同步方法,返回Task.CompletedTask只是形式上符合Task返回要求,但实际调用时会同步阻塞当前线程,不会在后台并行执行。Main方法中先调用Method会直接占满主线程,直到i递增到21亿才会执行Info,完全没有并行效果。 - 共享变量线程不安全:两个方法同时读写变量
i,没有任何同步机制,会引发竞态条件,导致读取的i值不准确(比如递增操作可能被CPU指令重排或中断,显示的数值不是最新的)。 - 取消逻辑不合理:Main中先
await task1,意味着要等Method执行完才会取消Info,不符合“并行运行直到手动终止”的需求。
修正方案
- 用
Task.Run将两个方法委托给线程池线程执行,实现真正的并行。 - 使用
Interlocked类处理共享变量i的递增和读取,保证线程安全。 - 调整Main中的等待逻辑,同时等待两个任务,支持通过
Q键主动终止所有任务。
修正后的完整代码
using System; using System.Threading; using System.Threading.Tasks; class Program { static void Method(ref int i, int target, CancellationToken cancellationToken) { while (Interlocked.CompareExchange(ref i, 0, 0) < target && !cancellationToken.IsCancellationRequested) { Interlocked.Increment(ref i); } } static void Info(ref int i, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { if (Console.KeyAvailable) { ConsoleKeyInfo keyInfo = Console.ReadKey(true); if (keyInfo.Key == ConsoleKey.I) { Console.Clear(); Console.WriteLine($"当前i值:{Interlocked.CompareExchange(ref i, 0, 0)}"); } else if (keyInfo.Key == ConsoleKey.Q) { cancellationToken.ThrowIfCancellationRequested(); } } Thread.Sleep(10); // 减少CPU空转消耗 } } static async Task Main() { int i = 0; using var cts = new CancellationTokenSource(); // 将两个方法放到线程池并行执行 Task task1 = Task.Run(() => Method(ref i, 2100000000, cts.Token), cts.Token); Task task2 = Task.Run(() => Info(ref i, cts.Token), cts.Token); try { // 等待任意一个任务完成(要么Method执行完,要么用户按Q取消) await Task.WhenAny(task1, task2); } catch (OperationCanceledException) { // 捕获取消异常,无需额外处理 } finally { cts.Cancel(); await Task.WhenAll(task1, task2); // 确保两个任务都正常结束 } Console.WriteLine("Готово"); Console.ReadKey(); } }
关键说明
Task.Run:把同步方法包装到线程池线程,让两个任务在后台并行执行,主线程不会被阻塞。Interlocked:通过原子操作保证i的递增和读取线程安全,避免竞态条件导致的数值错误。Task.WhenAny:等待任意一个任务完成(要么Method执行完毕,要么用户按Q触发取消),然后统一终止所有任务并等待它们结束。Thread.Sleep(10):降低Info方法的CPU占用,避免循环空转浪费资源。
内容的提问来源于stack exchange,提问作者mozay
相关产品推荐
相关产品推荐

