这是 C++11 之前很多争论的主题。
你的问题做了一个大胆的假设:分离显然是正确的行为。但你从来没有证实过它。事实上,有很多反对这个想法的论据,委员会也考虑过。
我将从outlining the argument against it论文中举这个例子:
int fib(int n) {
if (n <= 1) return n;
int fib1, fib2;
std::thread t([=, &fib1]{fib1 = fib(n-1);});
fib2 = fib(n-2);
if (fib2 < 0) throw ...
t.join();
return fib1 + fib2;
}
一旦你开始抛出异常,默认的分离行为就不再那么有用了。实际上,您可以想象一个更复杂的情况,其中异常来自线程创建例程的非本地内容。考虑this example from a later paper:
std::vector<std::pair<unsigned int, unsigned int>> partitions =
utils::partition_indexes(0, size-1, num_threads);
std::vector<std::thread> threads;
LOG(LOG_DEBUG, "controller::reload_all: starting reload threads...");
for (unsigned int i=0; i<num_threads-1; i++) {
threads.push_back(std::thread(reloadrangethread(this,
partitions[i].first, partitions[i].second, size, unattended)));
}
LOG(LOG_DEBUG, "controller::reload_all: starting my own reload...");
this->reload_range(partitions[num_threads-1].first,
partitions[num_threads-1].second, size, unattended);
LOG(LOG_DEBUG, "controller::reload_all: joining other threads...");
for (size_t i=0; i<threads.size(); i++) {
threads[i].join();
}
push_back 可能由于缺少用于重新分配数组的内存而失败。如果发生这种情况,您将无法访问所有这些线程,并且您的程序将被破坏。
这两种情况都会导致程序损坏,无论您是默认分离还是默认为terminate。但是,如果程序要被破坏,最好在问题发生时立即将其破坏,而不是在代码中稍后的某个位置。
现在,更安全的解决方案是在析构函数中使用join。但由于其他各种原因,这并没有发生。 (不幸的)共识是,如果你没有说出你想做什么,那么你的代码就会被破坏并且应该爆炸。
幸运的是,C++20 给了我们std::jthread,它默认加入了它的析构函数。
std::thread的析构行为的大致思路很简单:
- 用户没有说要做什么(即:没有打电话给
join 或detach)。
- 显然没有一个答案是正确的。
您声称简单地执行detach 是正确的解决方案。但为什么是对的?分离是一件非常不安全的事情,因为您再次无法使用线程join。
还有 RAII 的问题。异常可能会导致某些thread 对象无意中被破坏。如果发生这种情况,并且默认行为是分离,那么您的程序是否仍处于功能状态?如果您的程序的其余部分期望 join 那些线程,而现在这是不可能的呢?