为何使用Thread打开OracleConnection比Parallel.Foreach更快?
为什么首次无连接池时Parallel.Foreach比Thread慢这么多?
这个现象其实和Parallel.Foreach的默认调度策略以及首次运行时的瓶颈类型直接相关,咱们一步步拆解:
1. 首次运行的核心瓶颈:数据库连接建立(IO密集,而非CPU)
当没有连接池时,每个GET_DATA任务最耗时的操作是创建并打开Oracle连接——这是典型的IO密集型操作(等待数据库服务器响应、建立TCP连接、完成认证等),完全不依赖CPU计算。
- Thread方式的行为:你直接启动了25个线程,每个线程独立发起数据库连接请求。数据库服务器可以同时处理多个连接请求,所以这25个任务的IO操作是并行进行的,总耗时几乎等于单个连接建立的时间(3-5秒)。
- Parallel.Foreach的默认行为:Parallel类默认会根据你的CPU核心数来限制最大并发度。你的i5-6200U是双核四线程(超线程),所以默认的
MaxDegreeOfParallelism等于处理器数量(4)。这意味着Parallel会分批次处理任务:一次只跑4个任务,等这4个完成后再跑下一批。25个任务需要分7批(4×6=24,最后1批1个),总耗时就是单批耗时 × 批次数,大概是3-5秒 × 4 ≈ 12-15秒,正好和你观察到的结果一致。
2. 连接池建立后两者耗时相近的原因
当连接池创建完成后,每个任务不再需要耗时建立新连接,只是从池里获取现成的连接,执行select * from dual这种极轻量的查询——此时任务的瓶颈变成了CPU(线程调度、查询的少量计算)。
- Thread方式启动25个线程会导致大量的线程上下文切换,消耗额外的CPU资源;
- Parallel.Foreach的调度器会动态调整并发数,匹配CPU的处理能力,避免不必要的上下文切换,所以两者的耗时就拉平了。
3. 关于“单核心下Parallel慢近4倍”的猜测纠正
如果是真正的单核心CPU,Parallel的默认并发度是1,此时它会串行处理25个任务,总耗时会是Thread方式的25倍左右(因为Thread方式可以同时发起25个IO请求,虽然单核心会切换线程,但IO等待时线程会被挂起,不占用CPU,所以25个IO请求还是并行完成)。你说的“慢近4倍”其实是对应你当前四线程CPU的情况——Parallel默认限制4个并发,所以总耗时是Thread方式的4倍左右。
如何让Parallel.Foreach在IO密集场景下和Thread一样快?
你可以通过ParallelOptions手动设置最大并发度,让它一次性处理所有任务:
Parallel.ForEach( new int[] {0, 1, 2, ..., 24}, new ParallelOptions { MaxDegreeOfParallelism = 25 }, GET_DATA );
这样首次运行时,Parallel也会同时启动25个任务并行建立连接,耗时就会和Thread方式一致。
内容的提问来源于stack exchange,提问作者Martin Leung
相关产品推荐
相关产品推荐

