在使用线程时,以下几点十分重要:
- 实际能够同时运行的最大线程数是多少(考虑到你的硬件配置)?通常情况下,这个数目对应于系统上的逻辑处理器核心数。
- 如何减少线程启动和合并的频率?启动一个线程是一项昂贵的操作,线程所执行的工作应当超过启动它的成本。
- 如何尽量减少线程对同一数据的访问次数(尽管这一点对于你当前的具体情况相对不太重要)?
在你的案例中,我认为你最终创建的线程数量远远超过了系统实际上能够有效处理的数量。举个简单的例子,假如你在一台双核机器上运行程序,那么为了最大化吞吐量,你希望将工作分摊给两个线程。
你可以考虑以下改写后的代码:
void mergeSort(std::vector<int>& v, int left, int right, bool shouldThread) {
if (left < right) {
int mid = left + (right - left) / 2;
if (shouldThread) {
std::thread t1(mergeSort, std::ref(v), left, mid, false);
std::thread t2(mergeSort, std::ref(v), mid + 1, right, false);
t1.join();
t2.join();
}
else
{
mergeSort(std::ref(v), left, mid, false);
mergeSort(std::ref(v), mid + 1, right, false);
}
merge(v, left, mid, right);
}
}
这段代码会在第一层开始时跨两个线程执行,每个线程负责递归处理一半的子树。
假设这段代码有效(未经测试),下一步将是根据可用硬件资源来决定何时启动线程。但在那之前,建议先确保理解好这一层级的实现原理。