如何在C#中并行执行同步计算任务以充分利用多CPU资源?
在C#中如何并行执行同步计算任务以充分利用多CPU资源?
我手里有大量金融数据数组,需要为程序中的每个证券(任意时刻有10至100个代码)执行6个及以上基于该数组的纯计算同步函数。同步函数定义如下:
public double DoWork(double[] bigArray, double[] history) { double risk = 0; <calculate risk synchronously> return risk; }
我的初步思路是用Task.Run来并行执行,但觉得写法不够自然,因为Task.Run看起来像是要传入返回Task的函数:
var tasks = List<Task>(); tasks.Add(Task.Run(() => DoWork(bigArray, days30))); tasks.Add(Task.Run(() => DoWork(bigArray, days60))); tasks.Add(Task.Run(() => DoWork(bigArray, days90))); tasks.Add(Task.Run(() => DoWork(bigArray, days120))); await Task.WhenAll(tasks);
可行的解决方案
1. 修正Task.Run的用法(其实完全可用)
Task.Run本身支持接收同步委托,会自动把同步方法包装成Task<T>返回,你之前的写法只是没正确接收返回值。调整后更合理的写法:
var tasks = new List<Task<double>>(); tasks.Add(Task.Run(() => DoWork(bigArray, days30))); tasks.Add(Task.Run(() => DoWork(bigArray, days60))); tasks.Add(Task.Run(() => DoWork(bigArray, days90))); tasks.Add(Task.Run(() => DoWork(bigArray, days120))); // 等待所有任务完成并获取结果 double[] allResults = await Task.WhenAll(tasks);
这样既能并行执行,又能拿到每个任务的计算结果,写法其实很直接。
2. 用Parallel类(更适配CPU密集型任务)
因为你的任务是纯CPU计算,Parallel类是专门为这类场景设计的,它会自动根据CPU核心数优化并行调度,比手动管理Task更高效。
- 执行多个独立的计算任务:
double result30 = 0, result60 = 0, result90 = 0, result120 = 0; // 并行执行所有委托 Parallel.Invoke( () => result30 = DoWork(bigArray, days30), () => result60 = DoWork(bigArray, days60), () => result90 = DoWork(bigArray, days90), () => result120 = DoWork(bigArray, days120) ); // 之后直接使用result30、result60等变量
- 批量处理多个证券:
如果需要给每个证券都执行多组计算,用Parallel.ForEach更合适,配合线程安全容器存储结果:
// 假设你的证券集合,每个证券包含不同天数的history数据 var securities = GetAllSecurities(); // 线程安全字典,key为证券代码,value为多组计算结果 var calculationResults = new ConcurrentDictionary<string, double[]>(); Parallel.ForEach(securities, security => { var day30Risk = DoWork(bigArray, security.Days30History); var day60Risk = DoWork(bigArray, security.Days60History); var day90Risk = DoWork(bigArray, security.Days90History); // ...其他天数的计算 calculationResults.TryAdd(security.Code, new[] { day30Risk, day60Risk, day90Risk }); });
3. 用Task.Factory.StartNew(自定义并行配置)
如果需要更精细的控制(比如指定任务优先级、是否长时间运行),可以用Task.Factory.StartNew:
var task30 = Task.Factory.StartNew(() => DoWork(bigArray, days30), TaskCreationOptions.None); var task60 = Task.Factory.StartNew(() => DoWork(bigArray, days60), TaskCreationOptions.None); await Task.WhenAll(task30, task60); double risk30 = task30.Result; double risk60 = task60.Result;
注意事项
- 线程安全:你的
bigArray是只读的纯输入数据,所以不用担心并行访问的线程安全问题;如果涉及可写的共享数据,必须用线程安全容器(如ConcurrentDictionary)或手动加锁。 - 并行度控制:如果任务数量远多于CPU核心数,Parallel类会自动调整;也可以手动指定最大并行度,避免占用过多资源:
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount // 用当前CPU核心数 }; Parallel.ForEach(securities, options, security => { // 执行计算 });
- 避免无效并行:如果单个任务的计算量极小,并行调度的开销可能超过收益,这种情况建议串行执行;但金融计算通常计算量较大,并行是划算的。
内容的提问来源于stack exchange,提问作者user365462
相关产品推荐
相关产品推荐

